|
76207
|
1907
|
24
|
2026-04-24T07:36:37.033367+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016197033_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
482359671864130799
|
8408970457747008282
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
76208
|
1908
|
26
|
2026-04-24T07:36:37.117643+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016197117_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.59574467,"top":0.32322428,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.60538566,"top":0.3216281,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.61269945,"top":0.3216281,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
482359671864130799
|
8408970457747008282
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
76239
|
1908
|
42
|
2026-04-24T07:38:01.133389+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016281133_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Pushed 1 commit to origin/JY-20157-AJ-report-not-s Pushed 1 commit to origin/JY-20157-AJ-report-not-send-notification
text/html
text/html
text/html
View pull request
2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Pushed 1 commit to origin/JY-20157-AJ-report-not-send-notification","depth":3,"bounds":{"left":0.8753325,"top":0.82521945,"width":0.11037234,"height":0.027134877},"value":"Pushed 1 commit to origin/JY-20157-AJ-report-not-send-notification","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.82521945,"width":0.103390954,"height":0.027134877},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"View pull request","depth":2,"bounds":{"left":0.8753325,"top":0.8579409,"width":0.03523936,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.89944136,"width":0.100398935,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"bounds":{"left":0.8753325,"top":0.915403,"width":0.11037234,"height":0.027134877},"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.915403,"width":0.09507979,"height":0.027134877},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.59574467,"top":0.32322428,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.60538566,"top":0.3216281,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.61269945,"top":0.3216281,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"bounds":{"left":0.3799867,"top":0.0,"width":0.34375,"height":1.0},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.30851063,"top":0.05027933,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.31948137,"top":0.05027933,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.328125,"top":0.05027933,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.33676863,"top":0.05027933,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.34541222,"top":0.05027933,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-2823365913049337602
|
8408970457747008282
|
app_switch
|
accessibility
|
NULL
|
Pushed 1 commit to origin/JY-20157-AJ-report-not-s Pushed 1 commit to origin/JY-20157-AJ-report-not-send-notification
text/html
text/html
text/html
View pull request
2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
76795
|
1927
|
3
|
2026-04-24T08:26:22.359847+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777019182359_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20738-debug-AJ-tracking-UP, menu","depth":5,"help_text":"Git Branch: JY-20738-debug-AJ-tracking-UP","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8115828204200959479
|
8408970457747008282
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
76794
|
|
76796
|
1928
|
2
|
2026-04-24T08:26:22.361872+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777019182361_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20738-debug-AJ-tracking-UP, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.08510638,"height":0.025538707},"help_text":"Git Branch: JY-20738-debug-AJ-tracking-UP","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.63796544,"top":0.12529927,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6476064,"top":0.123703115,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.6549202,"top":0.123703115,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"bounds":{"left":0.3799867,"top":0.12210695,"width":0.34375,"height":0.87789303},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8115828204200959479
|
8408970457747008282
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
76874
|
1929
|
6
|
2026-04-24T08:31:04.180629+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777019464180_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
ReportControllerTest
Run 'ReportControllerTest'
Debug 'ReportControllerTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20738-debug-AJ-tracking-UP, menu","depth":5,"help_text":"Git Branch: JY-20738-debug-AJ-tracking-UP","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ReportControllerTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ReportControllerTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ReportControllerTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2249107635929059023
|
8408970457746877208
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
ReportControllerTest
Run 'ReportControllerTest'
Debug 'ReportControllerTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
76872
|
|
76875
|
1930
|
6
|
2026-04-24T08:31:04.277882+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777019464277_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
ReportControllerTest
Run 'ReportControllerTest'
Debug 'ReportControllerTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20738-debug-AJ-tracking-UP, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.08510638,"height":0.025538707},"help_text":"Git Branch: JY-20738-debug-AJ-tracking-UP","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.8400931,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"ReportControllerTest","depth":6,"bounds":{"left":0.85538566,"top":0.019952115,"width":0.06017287,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'ReportControllerTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'ReportControllerTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.5319149,"top":0.19952115,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.5415558,"top":0.19792499,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.54886967,"top":0.19792499,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"bounds":{"left":0.3799867,"top":0.05586592,"width":0.34375,"height":0.94413406},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"bounds":{"left":0.74202126,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.75398934,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.7613032,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2249107635929059023
|
8408970457746877208
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
ReportControllerTest
Run 'ReportControllerTest'
Debug 'ReportControllerTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
75763
|
1889
|
4
|
2026-04-24T06:47:39.949525+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777013259949_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"","depth":4,"value":"","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1144826565952075873
|
8408970389027400600
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
75761
|
|
75764
|
1890
|
4
|
2026-04-24T06:47:39.949543+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777013259949_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.59574467,"top":0.32322428,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.60538566,"top":0.3216281,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.61269945,"top":0.3216281,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"bounds":{"left":0.3799867,"top":0.3200319,"width":0.34375,"height":0.6799681},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"","depth":4,"bounds":{"left":0.63863033,"top":0.09736632,"width":0.34973404,"height":0.8818835},"value":"","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1144826565952075873
|
8408970389027400600
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
75762
|
|
75765
|
1890
|
5
|
2026-04-24T06:48:10.536199+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777013290536_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.59574467,"top":0.32322428,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.60538566,"top":0.3216281,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.61269945,"top":0.3216281,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"bounds":{"left":0.3799867,"top":0.3200319,"width":0.34375,"height":0.6799681},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"","depth":4,"bounds":{"left":0.63863033,"top":0.09736632,"width":0.34973404,"height":0.8818835},"value":"","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1144826565952075873
|
8408970389027400600
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
75766
|
1889
|
5
|
2026-04-24T06:48:11.053449+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777013291053_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"","depth":4,"value":"","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1144826565952075873
|
8408970389027400600
|
idle
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
75769
|
1889
|
7
|
2026-04-24T06:48:30.054322+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777013310054_m1.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"","depth":4,"value":"","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1144826565952075873
|
8408970389027400600
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
75770
|
1890
|
7
|
2026-04-24T06:48:29.955831+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777013309955_m2.jpg...
|
PhpStorm
|
faVsco.js – laravel.log
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.59574467,"top":0.32322428,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.60538566,"top":0.3216281,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.61269945,"top":0.3216281,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"bounds":{"left":0.3799867,"top":0.3200319,"width":0.34375,"height":0.6799681},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"","depth":4,"bounds":{"left":0.63863033,"top":0.09736632,"width":0.34973404,"height":0.8818835},"value":"","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
1144826565952075873
|
8408970389027400600
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#12011 on JY-20157-AJ-rep Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
176
|
5
|
23
|
2026-04-09T17:14:29.029793+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-09/1775 /Users/lukas/.screenpipe/data/data/2026-04-09/1775754869029_m1.jpg...
|
iTerm2
|
screenpipe"
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
whisper_init_state: compute buffer (encode) = 65 whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"whisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library","depth":4,"value":"whisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.16458334,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.16875,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.32916668,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.33333334,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.49340278,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.49756944,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Check today's app usage (node)","depth":2,"bounds":{"left":0.6576389,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.66180557,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.821875,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.82604164,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.95763886,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"screenpipe\"","depth":1,"bounds":{"left":0.4722222,"top":0.033333335,"width":0.058333334,"height":0.017777778},"role_description":"text"}]...
|
-2931038488646859087
|
8407166930568279521
|
manual
|
accessibility
|
NULL
|
whisper_init_state: compute buffer (encode) = 65 whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026...
|
NULL
|
|
177
|
5
|
24
|
2026-04-09T17:14:40.368110+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-09/1775 /Users/lukas/.screenpipe/data/data/2026-04-09/1775754880368_m1.jpg...
|
iTerm2
|
screenpipe"
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
whisper_init_state: compute buffer (cross) = 8 whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"whisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms","depth":4,"value":"whisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.16458334,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.16875,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.32916668,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.33333334,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.49340278,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.49756944,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Check today's app usage (node)","depth":2,"bounds":{"left":0.6576389,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.66180557,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.821875,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.82604164,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.95763886,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"screenpipe\"","depth":1,"bounds":{"left":0.4722222,"top":0.033333335,"width":0.058333334,"height":0.017777778},"role_description":"text"}]...
|
-5087223927363086600
|
8407166930568279521
|
click
|
accessibility
|
NULL
|
whisper_init_state: compute buffer (cross) = 8 whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run...
|
176
|
|
185
|
5
|
32
|
2026-04-09T17:14:54.138041+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-09/1775 /Users/lukas/.screenpipe/data/data/2026-04-09/1775754894138_m1.jpg...
|
iTerm2
|
screenpipe"
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
whisper_init_state: compute buffer (decode) = 96 whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"whisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events","depth":4,"value":"whisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.16458334,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.16875,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.32916668,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.33333334,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.49340278,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.49756944,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Check today's app usage (node)","depth":2,"bounds":{"left":0.6576389,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.66180557,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.821875,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.82604164,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
5123291467207642679
|
8407166930434061801
|
click
|
accessibility
|
NULL
|
whisper_init_state: compute buffer (decode) = 96 whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output...
|
184
|
|
187
|
5
|
34
|
2026-04-09T17:15:09.036508+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-09/1775 /Users/lukas/.screenpipe/data/data/2026-04-09/1775754909036_m1.jpg...
|
iTerm2
|
screenpipe"
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
whisper_backend_init_gpu: using Metal backend
ggml whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:18.727423Z INFO sc...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"whisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events\n2026-04-09T20:14:55.597138Z INFO screenpipe_audio::device::device_manager: starting recording for device: System Audio (output)\n2026-04-09T20:14:55.597260Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:14:55.974621Z INFO screenpipe_audio::device::device_manager: starting recording for device: soundcore AeroClip (input)\n2026-04-09T20:14:55.974675Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for soundcore AeroClip (input) (30s segments)","depth":4,"value":"whisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events\n2026-04-09T20:14:55.597138Z INFO screenpipe_audio::device::device_manager: starting recording for device: System Audio (output)\n2026-04-09T20:14:55.597260Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:14:55.974621Z INFO screenpipe_audio::device::device_manager: starting recording for device: soundcore AeroClip (input)\n2026-04-09T20:14:55.974675Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for soundcore AeroClip (input) (30s segments)","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.16458334,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.16875,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.32916668,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.33333334,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.49340278,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.49756944,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Check today's app usage (node)","depth":2,"bounds":{"left":0.6576389,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.66180557,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.821875,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.82604164,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.95763886,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"screenpipe\"","depth":1,"bounds":{"left":0.4722222,"top":0.033333335,"width":0.058333334,"height":0.017777778},"role_description":"text"}]...
|
-206579384513749361
|
8407166930417284585
|
visual_change
|
accessibility
|
NULL
|
whisper_backend_init_gpu: using Metal backend
ggml whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:18.727423Z INFO sc...
|
186
|
|
188
|
5
|
35
|
2026-04-09T17:15:15.048926+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-09/1775 /Users/lukas/.screenpipe/data/data/2026-04-09/1775754915048_m1.jpg...
|
iTerm2
|
screenpipe"
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
ggml_metal_init: allocating
ggml_metal_init: found ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcrib...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"ggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events\n2026-04-09T20:14:55.597138Z INFO screenpipe_audio::device::device_manager: starting recording for device: System Audio (output)\n2026-04-09T20:14:55.597260Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:14:55.974621Z INFO screenpipe_audio::device::device_manager: starting recording for device: soundcore AeroClip (input)\n2026-04-09T20:14:55.974675Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for soundcore AeroClip (input) (30s segments)\n2026-04-09T20:15:12.053186Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-206579384513749361, trigger=visual_change)","depth":4,"value":"ggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events\n2026-04-09T20:14:55.597138Z INFO screenpipe_audio::device::device_manager: starting recording for device: System Audio (output)\n2026-04-09T20:14:55.597260Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:14:55.974621Z INFO screenpipe_audio::device::device_manager: starting recording for device: soundcore AeroClip (input)\n2026-04-09T20:14:55.974675Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for soundcore AeroClip (input) (30s segments)\n2026-04-09T20:15:12.053186Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-206579384513749361, trigger=visual_change)","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.16458334,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.16875,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.32916668,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.33333334,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.49340278,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.49756944,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Check today's app usage (node)","depth":2,"bounds":{"left":0.6576389,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.66180557,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.821875,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.82604164,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.95763886,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"screenpipe\"","depth":1,"bounds":{"left":0.4722222,"top":0.033333335,"width":0.058333334,"height":0.017777778},"role_description":"text"}]...
|
3072302384101791222
|
8407166930417284585
|
visual_change
|
accessibility
|
NULL
|
ggml_metal_init: allocating
ggml_metal_init: found ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcrib...
|
NULL
|
|
189
|
5
|
36
|
2026-04-09T17:15:21.076773+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-09/1775 /Users/lukas/.screenpipe/data/data/2026-04-09/1775754921076_m1.jpg...
|
iTerm2
|
screenpipe"
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
ggml_metal_init: found device: Apple M1
ggml_metal ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for Sys...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"ggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events\n2026-04-09T20:14:55.597138Z INFO screenpipe_audio::device::device_manager: starting recording for device: System Audio (output)\n2026-04-09T20:14:55.597260Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:14:55.974621Z INFO screenpipe_audio::device::device_manager: starting recording for device: soundcore AeroClip (input)\n2026-04-09T20:14:55.974675Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for soundcore AeroClip (input) (30s segments)\n2026-04-09T20:15:12.053186Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-206579384513749361, trigger=visual_change)\n2026-04-09T20:15:18.079345Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=3072302384101791222, trigger=visual_change)","depth":4,"value":"ggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events\n2026-04-09T20:14:55.597138Z INFO screenpipe_audio::device::device_manager: starting recording for device: System Audio (output)\n2026-04-09T20:14:55.597260Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:14:55.974621Z INFO screenpipe_audio::device::device_manager: starting recording for device: soundcore AeroClip (input)\n2026-04-09T20:14:55.974675Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for soundcore AeroClip (input) (30s segments)\n2026-04-09T20:15:12.053186Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-206579384513749361, trigger=visual_change)\n2026-04-09T20:15:18.079345Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=3072302384101791222, trigger=visual_change)","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.16458334,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.16875,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.32916668,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.33333334,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.49340278,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.49756944,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Check today's app usage (node)","depth":2,"bounds":{"left":0.6576389,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.66180557,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.821875,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.82604164,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.95763886,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"screenpipe\"","depth":1,"bounds":{"left":0.4722222,"top":0.033333335,"width":0.058333334,"height":0.017777778},"role_description":"text"}]...
|
-6215291834790472374
|
8407166930417284585
|
visual_change
|
accessibility
|
NULL
|
ggml_metal_init: found device: Apple M1
ggml_metal ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for Sys...
|
188
|
|
191
|
5
|
38
|
2026-04-09T17:15:29.071929+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-09/1775 /Users/lukas/.screenpipe/data/data/2026-04-09/1775754929071_m1.jpg...
|
iTerm2
|
ffmpeg
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
ggml_metal_init: use fusion = true
ggml_me ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"ggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events\n2026-04-09T20:14:55.597138Z INFO screenpipe_audio::device::device_manager: starting recording for device: System Audio (output)\n2026-04-09T20:14:55.597260Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:14:55.974621Z INFO screenpipe_audio::device::device_manager: starting recording for device: soundcore AeroClip (input)\n2026-04-09T20:14:55.974675Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for soundcore AeroClip (input) (30s segments)\n2026-04-09T20:15:12.053186Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-206579384513749361, trigger=visual_change)\n2026-04-09T20:15:18.079345Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=3072302384101791222, trigger=visual_change)\n2026-04-09T20:15:25.327517Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6215291834790472374, trigger=click)\n2026-04-09T20:15:28.127631Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction: found 57 eligible frames","depth":4,"value":"ggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events\n2026-04-09T20:14:55.597138Z INFO screenpipe_audio::device::device_manager: starting recording for device: System Audio (output)\n2026-04-09T20:14:55.597260Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:14:55.974621Z INFO screenpipe_audio::device::device_manager: starting recording for device: soundcore AeroClip (input)\n2026-04-09T20:14:55.974675Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for soundcore AeroClip (input) (30s segments)\n2026-04-09T20:15:12.053186Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-206579384513749361, trigger=visual_change)\n2026-04-09T20:15:18.079345Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=3072302384101791222, trigger=visual_change)\n2026-04-09T20:15:25.327517Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6215291834790472374, trigger=click)\n2026-04-09T20:15:28.127631Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction: found 57 eligible frames","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.16458334,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.16875,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.32916668,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.33333334,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"ffmpeg","depth":2,"bounds":{"left":0.49340278,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.49756944,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Check today's app usage (node)","depth":2,"bounds":{"left":0.6576389,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.66180557,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.821875,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.82604164,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.95763886,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"ffmpeg","depth":1,"bounds":{"left":0.48333332,"top":0.033333335,"width":0.035416666,"height":0.017777778},"role_description":"text"}]...
|
2928604491856046970
|
8407166930417284585
|
clipboard
|
accessibility
|
NULL
|
ggml_metal_init: use fusion = true
ggml_me ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core...
|
190
|
|
52538
|
1137
|
23
|
2026-04-20T07:21:26.039285+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776669686039_m2.jpg...
|
PhpStorm
|
faVsco.js – console [EU]
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Laravel Idea: Generate Helper Code?
Helper Code wi Laravel Idea: Generate Helper Code?
Helper Code will help IDE to understand your Laravel app code.
text/html
text/html
text/html
Generate
Don't Show Anymore
More
The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.
text/html
text/html
text/html
Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Laravel Idea: Generate Helper Code?","depth":2,"bounds":{"left":0.8753325,"top":0.8172386,"width":0.100398935,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"Helper Code will help IDE to understand your Laravel app code.","depth":3,"bounds":{"left":0.8753325,"top":0.83320034,"width":0.11037234,"height":0.027134877},"value":"Helper Code will help IDE to understand your Laravel app code.","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.83320034,"width":0.09275266,"height":0.027134877},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Generate","depth":2,"bounds":{"left":0.8753325,"top":0.8659218,"width":0.018949468,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Don't Show Anymore","depth":2,"bounds":{"left":0.89960104,"top":0.8659218,"width":0.04288564,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"More","depth":2,"bounds":{"left":0.27027926,"top":1.0,"width":0.016289894,"height":0.0},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.","depth":3,"bounds":{"left":0.8753325,"top":0.9074222,"width":0.11037234,"height":0.054269753},"value":"The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.9074222,"width":0.095744684,"height":0.054269753},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11894 on JY-18909-automated-reports-ask-jiminny, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #11894 exists for current branch JY-18909-automated-reports-ask-jiminny","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.36868352,"top":0.2490024,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"bounds":{"left":0.37865692,"top":0.2490024,"width":0.008976064,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.38929522,"top":0.24740623,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.39660904,"top":0.24740623,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.40492022,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.41356382,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.4245346,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.4331782,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.4418218,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.45279256,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"bounds":{"left":0.4637633,"top":0.09896249,"width":0.024268618,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"bounds":{"left":0.49035904,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"bounds":{"left":0.5013298,"top":0.09896249,"width":0.029587766,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"bounds":{"left":0.69913566,"top":0.09896249,"width":0.02825798,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
3161242233991905402
|
8406718657933331226
|
visual_change
|
accessibility
|
NULL
|
Laravel Idea: Generate Helper Code?
Helper Code wi Laravel Idea: Generate Helper Code?
Helper Code will help IDE to understand your Laravel app code.
text/html
text/html
text/html
Generate
Don't Show Anymore
More
The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.
text/html
text/html
text/html
Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification...
|
NULL
|
|
72869
|
1778
|
7
|
2026-04-23T06:20:20.846438+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776925220846_m1.jpg...
|
PhpStorm
|
faVsco.js – UserTransformer.php
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
12
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"12","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"36","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"}]...
|
-1229504928616003175
|
8406718389802436924
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
12
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class...
|
NULL
|
|
72870
|
1779
|
10
|
2026-04-23T06:20:21.419002+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776925221419_m2.jpg...
|
PhpStorm
|
faVsco.js – UserTransformer.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
12
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.10139628,"height":0.025538707},"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.7972075,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"bounds":{"left":0.8125,"top":0.019952115,"width":0.10305851,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"12","depth":4,"bounds":{"left":0.66921544,"top":0.15003991,"width":0.009640957,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.68085104,"top":0.15003991,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.69049203,"top":0.14844373,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.6978058,"top":0.14844373,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"36","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregator.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregatorInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatabaseActivities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatasourceInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivityInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Comments","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Forecast","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Jobs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"QueryBuilder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Services","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ClosingPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CreatedPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Criteria.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaNormalizer.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmServiceInterface.php, interface","depth":10,"role_description":"text"}]...
|
105273890682564351
|
8406718389801912700
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
12
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface...
|
NULL
|
|
72871
|
1778
|
8
|
2026-04-23T06:20:22.962337+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-23/1776 /Users/lukas/.screenpipe/data/data/2026-04-23/1776925222962_m1.jpg...
|
PhpStorm
|
faVsco.js – UserTransformer.php
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Git Branch: JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"RequestGenerateAskJiminnyReportJobTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'RequestGenerateAskJiminnyReportJobTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"13","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"2","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","depth":4,"value":"<?php\n\nnamespace Jiminny\\Http\\Transformers;\n\nuse Illuminate\\Contracts\\Container\\Container;\nuse Illuminate\\Support\\Collection;\nuse Jiminny\\Component\\Sidekick\\SidekickService;\nuse Jiminny\\Exceptions\\ActivityProviderException;\nuse Jiminny\\Http\\Controllers\\Settings\\Users\\Utils\\UserSetting;\nuse Jiminny\\Models\\Activity\\Provider;\nuse Jiminny\\Models\\Feature\\FeatureEnum;\nuse Jiminny\\Models\\JobTitle;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Repositories\\AutomatedReportsRepository;\nuse Jiminny\\Repositories\\UserRepository;\nuse Jiminny\\Services\\Notification\\Messengers\\MsTeams;\nuse Jiminny\\Services\\UserService;\nuse League\\Fractal\\Resource;\nuse League\\Fractal\\Resource\\Item;\nuse League\\Fractal\\TransformerAbstract;\n\nclass UserTransformer extends TransformerAbstract\n{\n protected array $availableIncludes = [\n 'team',\n 'group',\n 'job',\n 'roles',\n 'permissions',\n ];\n\n private Container $container;\n\n private bool $withSelfVisibility = false;\n\n public function __construct(?Container $container = null)\n {\n $this->container = $container ?? app();\n }\n\n public function withSelfVisibility(): self\n {\n $this->withSelfVisibility = true;\n\n return $this;\n }\n\n /**\n * @throws ActivityProviderException\n *\n * @return array<string, mixed>\n */\n public function transform(User $user): array\n {\n $attributes = [\n 'id' => $user->getUuid(),\n 'name' => $user->getName(),\n 'firstName' => $user->getFirstName(),\n 'photoUrl' => $user->getPhotoUrl(),\n 'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),\n 'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),\n // DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!\n 'crmRequired' => $user->crm_required,\n 'slackFollowUp' => $user->slack_follow_up,\n ];\n\n // DO NOT USE User::getId as it is not hydrated when fetched from ES!\n if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {\n $softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()\n && $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()\n ;\n\n $conferenceSidekickOpen = $user->getConferenceSidekickOpen();\n $softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();\n $conferenceSidekickPopupOverridden = false;\n $softphoneSidekickPopupOverridden = false;\n $hasSidekickEnabled = true;\n\n if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {\n $sidekickService = $this->getSidekickService();\n\n $sidekickData = $sidekickService->getSidekickSettingsForUser($user);\n\n $conferenceSidekickOpen = $sidekickData['conferenceSettings'];\n $softphoneSidekickOpen = $sidekickData['softphoneSettings'];\n $conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];\n $softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];\n $hasSidekickEnabled = $sidekickData['sidekickEnabled'];\n }\n\n $userService = $this->getUserService();\n $dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);\n $dataFormatCountryCode = $userService->getDateTimeCountryCode($user);\n\n // Attributes for the user only.\n $attributes += [\n 'conferenceJoinReminder' => $user->conference_join_reminder,\n 'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),\n 'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),\n 'softphoneInboundDestination' => $user->softphone_inbound_destination,\n 'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,\n 'softphoneNumber' => $user->getSoftPhoneNumber(),\n 'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),\n 'email' => $user->getEmailAddress(),\n 'secondaryEmail' => $user->getSecondaryEmailAddress(),\n 'phone' => $user->phone,\n 'secondaryPhone' => $user->secondary_phone,\n 'callerId' => $user->getCallerId(),\n 'countryCode' => $user->getCountryCode(),\n 'timezone' => $user->getTimezone()->getName(),\n 'language' => $user->getLanguage(),\n 'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),\n 'status' => $user->getStatus(),\n 'hash' => $user->generateHash(),\n 'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,\n 'notifyLiveCoaching' => $user->notify_live_coaching,\n 'activityLogReminder' => $user->activity_log_reminder,\n 'conferenceSidekickOpen' => $conferenceSidekickOpen,\n 'softphoneSidekickOpen' => $softphoneSidekickOpen,\n 'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,\n 'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,\n 'hasSidekickEnabled' => $hasSidekickEnabled,\n 'activityActionItems' => $user->activity_action_items,\n 'syncEmail' => $user->isSyncEmailEnabled(),\n 'syncConference' => $user->sync_conference,\n 'syncDialer' => $user->shouldSyncDialer(),\n 'needsToConfigurePhoneNumber' => $this->container\n ->get('onboarding_phone_decider')\n ->isOnboardable($user),\n 'shouldShowPhoneNumberField' => $this->container\n ->get('onboarding_phone_decider')\n ->shouldShowPhoneNumberField($user),\n UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,\n 'countryByTimezone' => $dataFormatCountryCode,\n 'conferenceSlug' => $user->getConferenceSlug(),\n 'conferenceRecordExternalOrganizerPreference' =>\n $userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),\n 'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,\n 'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),\n ];\n\n if ($user->softphone_debug) {\n $attributes += [\n 'debugSoftphone' => $user->softphone_debug, // Needed?\n ];\n }\n }\n\n if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {\n $socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);\n $state = $socialAccountMS !== null\n ? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)\n : null;\n\n $attributes['integrations']['office'] = [\n 'displayName' => 'Microsoft Teams',\n 'apiName' => 'microsoft-teams',\n 'types' => ['notification'],\n 'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,\n 'logo' => cdn('img/ms-teams-logo.svg'),\n 'installationStrategy' => 'oauth',\n ];\n }\n\n return $attributes;\n }\n\n public function includeTeam(User $user): Item\n {\n $team = $user->getTeam();\n\n return $this->item($team, $this->getTeamTransformer());\n }\n\n public function includeGroup(User $user): ?Item\n {\n $group = $user->getGroup();\n if ($group === null) {\n return null;\n }\n\n return $this->item($group, $this->getGroupTransformer());\n }\n\n public function includeJob(User $user): ?Item\n {\n $job = $user->getJobTitle();\n\n if (! $job instanceof JobTitle) {\n return null;\n }\n\n return $this->item($job, $this->getJobTitleTransformer());\n }\n\n public function includeRoles(User $user): Resource\\Collection\n {\n /** @var Collection<int, string> $roles */\n $roles = $user->roles()\n ->where('is_visible', true)\n ->pluck('name')\n ->toArray();\n\n return $this->collection($roles, $this->getRoleTransformer());\n }\n\n public function includePermissions(User $user): Resource\\Collection\n {\n $permissions = $user->allPermissions();\n\n return $this->collection($permissions, $this->getPermissionTransformer());\n }\n\n public function includeIntegrations(User $user): Item\n {\n return $this->item($user, $this->getIntegrationsTransformer());\n }\n\n private function getTeamTransformer(): TransformerAbstract\n {\n return $this->container->get(TeamTransformer::class);\n }\n\n private function getGroupTransformer(): GroupTransformer\n {\n return $this->container->get(GroupTransformer::class);\n }\n\n private function getIntegrationsTransformer(): IntegrationTransformer\n {\n return $this->container->get(IntegrationTransformer::class);\n }\n\n private function getPermissionTransformer(): PermissionTransformer\n {\n return $this->container->get(PermissionTransformer::class);\n }\n\n private function getRoleTransformer(): RoleTransformer\n {\n return $this->container->get(RoleTransformer::class);\n }\n\n private function getJobTitleTransformer(): JobTitleTransformer\n {\n return $this->container->get(JobTitleTransformer::class);\n }\n\n private function getSidekickService(): SidekickService\n {\n /** @var SidekickService */\n return $this->container->get(SidekickService::class);\n }\n\n private function getUserService(): UserService\n {\n /** @var UserService */\n return $this->container->get(UserService::class);\n }\n\n private function getAutomatedReportsRepository(): AutomatedReportsRepository\n {\n /** @var AutomatedReportsRepository */\n return $this->container->get(AutomatedReportsRepository::class);\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"36","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Component\\DealInsights;\n\nuse Doctrine\\DBAL\\Connection;\nuse Generator;\nuse Illuminate\\Support\\Collection;\nuse Illuminate\\Support\\Facades\\DB;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealData;\nuse Jiminny\\Component\\DealInsights\\Forecast\\DealsFilter;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\QueryBuilder;\nuse Jiminny\\Component\\DealInsights\\QueryBuilder\\Visitor\\QueryBuilderVisitorInterface;\nuse Jiminny\\Contracts\\Services\\Crm\\ServiceInterface;\nuse Jiminny\\Exceptions\\SocialAccountTokenInvalidException;\nuse Jiminny\\Models\\Activity;\nuse Jiminny\\Models\\Crm\\Configuration;\nuse Jiminny\\Models\\Crm\\Field;\nuse Jiminny\\Models\\Stage;\nuse Jiminny\\Models\\Team;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Models;\nuse Jiminny\\Services\\Crm\\IntegrationApp\\DTO\\Utils\\UrlGeneratorInterface;\nuse Jiminny\\Services\\Crm\\ProviderRegistry;\nuse Jiminny\\Traits\\RequiresUUID;\nuse Illuminate\\Database\\Query\\Builder;\nuse Illuminate\\Database\\Eloquent;\nuse Psr\\Container\\ContainerExceptionInterface;\nuse Psr\\Container\\NotFoundExceptionInterface;\n\nclass DealsRepository implements DealsRepositoryInterface\n{\n private Connection $connection;\n\n private ProviderRegistry $providerRegistry;\n\n /**\n * @var QueryBuilderVisitorInterface[]\n */\n private array $visitors = [];\n\n /**\n * @param QueryBuilderVisitorInterface[] $visitors\n */\n public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])\n {\n $this->connection = $connection;\n $this->providerRegistry = $crmProviderRegistry;\n\n foreach ($visitors as $visitor) {\n $this->visitors[$visitor->getIdentifier()] = $visitor;\n }\n }\n\n public function getDeals(CriteriaInterface $criteria): array\n {\n $context = $criteria->getContext();\n $team = $context->getTeam();\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n\n $this->visit($qb, $criteria);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getDeal(Team $team, int $id): array\n {\n $crmService = $this->getCrmService($team);\n\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);\n $qb = $this->getSearchSelectAndWhereClauses($qb);\n $qb->andWhere('opp.id = :id')->setParameter('id', $id);\n\n return $this->execute($team, $crmService, $qb);\n }\n\n public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')\n ->from('crm_fields', 'f')\n ->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')\n ->where('f.crm_configuration_id = :crm')\n ->andWhere('f.object_type = :type')\n ->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')\n ->orderBy('fd.object_id', 'ASC')\n ->addOrderBy('fd.updated_at', 'ASC')\n\n ->setParameter('type', Field::OBJECT_OPPORTUNITY)\n ->setParameter('crm', $crmId)\n ;\n\n if (! empty($crmFields)) {\n $fields = array_map(fn ($value): string => '\"' . $value . '\"', $crmFields);\n $qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');\n }\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAssociative();\n }\n\n public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array\n {\n $qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);\n\n $qb\n ->select('COALESCE(opp.currency_code, \"' . $defaultCurrency . '\") AS currency')\n ->addSelect('SUM(opp.value) as total')\n ->addSelect('count(*) as `count`')\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'))\n ->groupBy('currency')\n ;\n\n $this->visit($qb, $criteria);\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n public function getDealActivities(CriteriaInterface $criteria): array\n {\n $qb = Activity::with(['participants', 'user'])\n ->where('opportunity_id', $criteria->getOpportunityId())\n ->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())\n ->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())\n ->orderBy($criteria->getSortBy(), $criteria->getSortDirection())\n ;\n\n // Should we filter activities by criteria? It's intended to filter deals.\n\n return $qb->get()->all();\n }\n\n public function getStages(CriteriaInterface $criteria): array\n {\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('id', 'label', 'sequence')\n ->from('stages', 's')\n ->where('crm_configuration_id = :crm_configuration_id')\n ->andWhere('type = :type')\n ->orderBy('sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())\n ->setParameter('type', Stage::TYPE_OPPORTUNITY);\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $result[$row['id']] = [\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n public function getConfigurationStages(Configuration $configuration): Collection\n {\n return $configuration\n ->stages()\n ->where('type', Stage::TYPE_OPPORTUNITY)\n ->get();\n }\n\n public function getPipelineData(Configuration $crm): array\n {\n $qb = new QueryBuilder($this->connection);\n $provider = $crm->provider;\n\n $qb\n ->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')\n ->from('stages', 's')\n ->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')\n ->where('s.crm_configuration_id = :crm_configuration_id')\n ->andWhere('s.type = :type')\n ->orderBy('bps.business_process_id', 'ASC')\n ->addOrderBy('s.sequence', 'ASC')\n\n ->setParameter('crm_configuration_id', $crm->id)\n ->setParameter('type', Stage::TYPE_OPPORTUNITY)\n ;\n\n $result = [];\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];\n $result[$row['pipeline_id']][] = [\n 'value' => $value,\n 'label' => $row['label'],\n 'sequence' => $row['sequence'],\n ];\n }\n\n return $result;\n }\n\n private function createQueryBuilder(string $realm): QueryBuilder\n {\n return (new QueryBuilder($this->connection))\n ->setRealm($realm)\n ->from('opportunities', 'opp')\n ->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')\n ->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')\n ->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')\n ;\n }\n\n /**\n * Applies all applicable visitors and returns the IDs of the executed ones\n *\n * @return string[]\n */\n private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array\n {\n $queryVisitors = [];\n\n foreach ($this->visitors as $visitor) {\n if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {\n $visitor->visit($queryBuilder, $criteria);\n\n $queryVisitors[] = $visitor->getIdentifier();\n }\n }\n\n return $queryVisitors;\n }\n\n private function hydrateStages(array $deals): array\n {\n foreach ($this->fetchStages(array_keys($deals)) as $stage) {\n $oppId = (int) $stage['opportunity_id'];\n\n if (! isset($deals[$oppId])) {\n continue; // or throw??!\n }\n\n $deals[$oppId]['stages'][] = [\n 'id' => $stage['stage_id'],\n 'name' => $stage['label'],\n 'enteredAt' => $stage['created_at'],\n ];\n }\n\n return $deals;\n }\n\n /**\n * @param int[] $dealIds\n */\n private function fetchStages(array $dealIds): array\n {\n if (empty($dealIds)) {\n return [];\n }\n\n $qb = new QueryBuilder($this->connection);\n\n $qb\n ->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')\n ->from('opportunity_stages', 'os')\n ->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')\n ->where($qb->expr()->in('os.opportunity_id', $dealIds))\n ->orderBy('os.opportunity_id', 'ASC')\n ->addOrderBy('s.created_at', 'ASC')\n ;\n\n return $qb->executeQuery()->fetchAllAssociative();\n }\n\n private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array\n {\n $result = [];\n\n foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {\n $data = [\n 'uuid' => RequiresUUID::toNormal($row['uuid']),\n 'name' => $row['name'],\n 'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),\n 'account' => [\n 'name' => $row['acc_name'],\n 'url' => $crmService->generateProviderUrl(\n providerId: $row['acc_provider_id'],\n objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'\n ),\n ],\n 'owner' => null,\n 'rawValue' => [\n 'amount' => (float) $row['value'],\n 'currency' => $row['currency_code'],\n ],\n 'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),\n 'openDate' => $row['remotely_created_at'] ?? null,\n 'closeDate' => $row['close_date'] ?? null,\n 'stages' => [],\n 'currentPipelineId' => $row['pipeline_id'],\n 'currentStage' => [\n 'id' => $row['stage_id'],\n 'enteredAt' => $row['stage_updated_at'],\n ],\n 'currentStageUpdatedAt' => $row['stage_updated_at'],\n 'isClosed' => (bool) $row['is_closed'],\n 'isWon' => (bool) $row['is_won'],\n ];\n\n if (isset($row['owner_uuid'])) {\n $data['owner'] = [\n 'uuid' => RequiresUUID::toNormal($row['owner_uuid']),\n 'name' => $row['owner_name'],\n 'photoUrl' => $row['owner_photo'] === null\n ? null\n : client_cdn($row['owner_photo'], $team),\n 'id' => $row['owner_id'],\n 'job' => $row['owner_job'],\n ];\n }\n\n $result[(int) $row['opp_id']] = $data;\n }\n\n return $this->hydrateStages($result);\n }\n\n private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder\n {\n $qb = clone $queryBuilder;\n $qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');\n\n $qb\n ->select(...[\n 'opp.id as opp_id',\n 'opp.uuid',\n 'opp.name',\n 'opp.value',\n 'opp.currency_code',\n 'opp.close_date',\n 'opp.remotely_created_at',\n 'opp.is_closed',\n 'opp.is_won',\n ])\n ->addSelect(...[\n 'usr.uuid as owner_uuid',\n 'usr.name AS owner_name',\n 'usr.photo_path as owner_photo',\n 'usr.id AS owner_id',\n 'jt.name as owner_job',\n ])\n ->addSelect('opp.stage_id', 'opp.stage_updated_at')\n ->addSelect(...[\n 'acc.name AS acc_name',\n 'acc.is_internal as acc_is_internal',\n 'opp.stage_updated_at',\n 'acc.crm_provider_id AS acc_provider_id',\n 'opp.crm_provider_id AS opp_provider_id',\n ])\n ->addSelect('rt.business_process_id AS pipeline_id')\n\n ->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users\n ->andWhere($qb->expr()->isNull('opp.deleted_at'));\n\n return $qb;\n }\n\n /**\n * @throws ContainerExceptionInterface\n * @throws NotFoundExceptionInterface\n * @throws SocialAccountTokenInvalidException\n */\n private function getCrmService(Team $team): ServiceInterface\n {\n $crmService = $this->providerRegistry->get($team->crm->provider);\n $crmService->setConfiguration($team->crm);\n if ($crmService instanceof UrlGeneratorInterface) {\n $crmService->setCrmUrlGenerator($team->crm);\n }\n\n return $crmService;\n }\n\n /**\n *\n * @return Generator<DealData>\n */\n public function getForecastData(DealsFilter $filter): Generator\n {\n $opportunities = DB::query()\n ->select([\n 'o.value',\n 'o.close_date',\n 'o.currency_code',\n 'o.is_won',\n 'o.is_closed',\n 'o.probability',\n 'o.forecast_category',\n ])\n ->from('opportunities', 'o')\n ->join('users', 'users.id', '=', 'o.user_id')\n ->join('groups', 'groups.id', '=', 'users.group_id')\n ->where('users.team_id', $filter->getTeam()->getId())\n ->where('o.close_date', '>=', $filter->getStartDate())\n ->where('o.close_date', '<=', $filter->getEndDate())\n ->where('o.currency_code', $filter->getCurrency())\n ->where('o.deleted_at', '=', null)\n ;\n\n $userUuidList = $filter->getUserUuidList();\n if (! empty($userUuidList)) {\n $userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);\n\n $opportunities->whereIn('users.uuid', $userUuidList);\n }\n\n $groupUuidList = $filter->getGroupUuidList();\n if (! empty($groupUuidList)) {\n $groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);\n\n $opportunities->whereIn('groups.uuid', $groupUuidList);\n }\n\n foreach ($opportunities->cursor() as $row) {\n yield new DealData(\n (float) $row->value,\n $row->close_date,\n ! empty($row->is_won),\n ! empty($row->is_closed),\n $row->probability ?: 0,\n $row->forecast_category ?: '',\n );\n }\n }\n\n public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection\n {\n return $user->subscriptionSets()\n ->where(static function (Eloquent\\Builder $query): void {\n $query\n ->whereNull('expired_at')\n ->orWhere('expired_at', '>=', now());\n })\n ->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {\n $join\n ->on('subscription_set_id', '=', 'activity_subscription_sets.id');\n $join\n ->where('followable_type', Models\\Activity\\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)\n ->whereIn('followable_id', $opportunityIds);\n })\n ->pluck('followable_id');\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregator.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAggregatorInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatabaseActivities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"DatasourceInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivity.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"RelatedActivityInterface.php, interface","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Commands","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Comments","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Forecast","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Jobs","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"QueryBuilder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Services","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"ClosingPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CreatedPeriodOptionDecorator.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Criteria.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CriteriaNormalizer.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"CrmServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealContactService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealInsightsCriteriaBuilder.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealServiceInterface.php, interface","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepository.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"DealsRepositoryInterface.php, interface","depth":10}]...
|
-5380076283229952570
|
8406683239789562236
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20157-AJ-report-not-se Project: faVsco.js, menu
JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
RequestGenerateAskJiminnyReportJobTest
Run 'RequestGenerateAskJiminnyReportJobTest'
Debug 'RequestGenerateAskJiminnyReportJobTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
13
2
Previous Highlighted Error
Next Highlighted Error
<?php
namespace Jiminny\Http\Transformers;
use Illuminate\Contracts\Container\Container;
use Illuminate\Support\Collection;
use Jiminny\Component\Sidekick\SidekickService;
use Jiminny\Exceptions\ActivityProviderException;
use Jiminny\Http\Controllers\Settings\Users\Utils\UserSetting;
use Jiminny\Models\Activity\Provider;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\JobTitle;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Notification\Messengers\MsTeams;
use Jiminny\Services\UserService;
use League\Fractal\Resource;
use League\Fractal\Resource\Item;
use League\Fractal\TransformerAbstract;
class UserTransformer extends TransformerAbstract
{
protected array $availableIncludes = [
'team',
'group',
'job',
'roles',
'permissions',
];
private Container $container;
private bool $withSelfVisibility = false;
public function __construct(?Container $container = null)
{
$this->container = $container ?? app();
}
public function withSelfVisibility(): self
{
$this->withSelfVisibility = true;
return $this;
}
/**
* @throws ActivityProviderException
*
* @return array<string, mixed>
*/
public function transform(User $user): array
{
$attributes = [
'id' => $user->getUuid(),
'name' => $user->getName(),
'firstName' => $user->getFirstName(),
'photoUrl' => $user->getPhotoUrl(),
'conferenceRecordPreference' => $user->checkConferenceRecordPreference(),
'conferenceRecordInternalPreference' => $user->checkConferenceRecordInternalPreference(),
// DO NOT USE User::isCrmRequired as it is not hydrated when fetched from ES!
'crmRequired' => $user->crm_required,
'slackFollowUp' => $user->slack_follow_up,
];
// DO NOT USE User::getId as it is not hydrated when fetched from ES!
if ($this->withSelfVisibility || (auth()->check() && auth()->user()->id === $user->id)) {
$softphoneHasVoiceCapability = $user->hasSoftphoneNumberCapabilities()
&& $user->getSoftphoneNumberCapabilities()->hasVoiceCapability()
;
$conferenceSidekickOpen = $user->getConferenceSidekickOpen();
$softphoneSidekickOpen = $user->getSoftphoneSidekickOpen();
$conferenceSidekickPopupOverridden = false;
$softphoneSidekickPopupOverridden = false;
$hasSidekickEnabled = true;
if ($user->getTeam()->hasFeature(FeatureEnum::SIDEKICK_SETTINGS)) {
$sidekickService = $this->getSidekickService();
$sidekickData = $sidekickService->getSidekickSettingsForUser($user);
$conferenceSidekickOpen = $sidekickData['conferenceSettings'];
$softphoneSidekickOpen = $sidekickData['softphoneSettings'];
$conferenceSidekickPopupOverridden = $sidekickData['conferenceSidekickPopupOverridden'];
$softphoneSidekickPopupOverridden = $sidekickData['softphoneSidekickPopupOverridden'];
$hasSidekickEnabled = $sidekickData['sidekickEnabled'];
}
$userService = $this->getUserService();
$dealInsightsPeriod = $userService->getDealInsightTimelinePeriod($user);
$dataFormatCountryCode = $userService->getDateTimeCountryCode($user);
// Attributes for the user only.
$attributes += [
'conferenceJoinReminder' => $user->conference_join_reminder,
'softphoneInboundRecordPreference' => $user->checkSoftphoneInboundRecordPreference(),
'softphoneOutboundRecordPreference' => $user->checkSoftphoneOutboundRecordPreference(),
'softphoneInboundDestination' => $user->softphone_inbound_destination,
'softphoneHasVoiceCapability' => $softphoneHasVoiceCapability,
'softphoneNumber' => $user->getSoftPhoneNumber(),
'formattedSoftphoneNumber' => $user->getFormattedSoftphoneNumberAttribute(),
'email' => $user->getEmailAddress(),
'secondaryEmail' => $user->getSecondaryEmailAddress(),
'phone' => $user->phone,
'secondaryPhone' => $user->secondary_phone,
'callerId' => $user->getCallerId(),
'countryCode' => $user->getCountryCode(),
'timezone' => $user->getTimezone()->getName(),
'language' => $user->getLanguage(),
'locales' => $this->container->get(UserRepository::class)->getUserLocales($user),
'status' => $user->getStatus(),
'hash' => $user->generateHash(),
'registrationDate' => $user->created_at ? $user->created_at->toIso8601String() : null,
'notifyLiveCoaching' => $user->notify_live_coaching,
'activityLogReminder' => $user->activity_log_reminder,
'conferenceSidekickOpen' => $conferenceSidekickOpen,
'softphoneSidekickOpen' => $softphoneSidekickOpen,
'conferenceSidekickPopupOverridden' => $conferenceSidekickPopupOverridden,
'softphoneSidekickPopupOverridden' => $softphoneSidekickPopupOverridden,
'hasSidekickEnabled' => $hasSidekickEnabled,
'activityActionItems' => $user->activity_action_items,
'syncEmail' => $user->isSyncEmailEnabled(),
'syncConference' => $user->sync_conference,
'syncDialer' => $user->shouldSyncDialer(),
'needsToConfigurePhoneNumber' => $this->container
->get('onboarding_phone_decider')
->isOnboardable($user),
'shouldShowPhoneNumberField' => $this->container
->get('onboarding_phone_decider')
->shouldShowPhoneNumberField($user),
UserSetting::DEAL_INSIGHTS_TIMELINE_PERIOD => $dealInsightsPeriod,
'countryByTimezone' => $dataFormatCountryCode,
'conferenceSlug' => $user->getConferenceSlug(),
'conferenceRecordExternalOrganizerPreference' =>
$userService->isConferenceRecordExternalOrganizerPreferenceEnabled($user),
'hasGeneratedAiReports' => $this->getAutomatedReportsRepository()->countUserReports($user) > 0,
'sendEmailWhenExportLinkIsOpened' => $userService->canSendEmailWhenExportLinkIsOpened($user),
];
if ($user->softphone_debug) {
$attributes += [
'debugSoftphone' => $user->softphone_debug, // Needed?
];
}
}
if ($user->getTeam()->getNotificationProvider() === Team::NOTIFICATION_PROVIDER_MSTEAMS) {
$socialAccountMS = $user->getSocialAccount(Team::CALENDAR_PROVIDER_OFFICE);
$state = $socialAccountMS !== null
? str_contains($socialAccountMS->auth_scope, MsTeams::SCOPE_CHAT_CREATE)
: null;
$attributes['integrations']['office'] = [
'displayName' => 'Microsoft Teams',
'apiName' => 'microsoft-teams',
'types' => ['notification'],
'state' => $state ? Provider::STATE_INSTALLED : Provider::STATE_NOT_INSTALLED,
'logo' => cdn('img/ms-teams-logo.svg'),
'installationStrategy' => 'oauth',
];
}
return $attributes;
}
public function includeTeam(User $user): Item
{
$team = $user->getTeam();
return $this->item($team, $this->getTeamTransformer());
}
public function includeGroup(User $user): ?Item
{
$group = $user->getGroup();
if ($group === null) {
return null;
}
return $this->item($group, $this->getGroupTransformer());
}
public function includeJob(User $user): ?Item
{
$job = $user->getJobTitle();
if (! $job instanceof JobTitle) {
return null;
}
return $this->item($job, $this->getJobTitleTransformer());
}
public function includeRoles(User $user): Resource\Collection
{
/** @var Collection<int, string> $roles */
$roles = $user->roles()
->where('is_visible', true)
->pluck('name')
->toArray();
return $this->collection($roles, $this->getRoleTransformer());
}
public function includePermissions(User $user): Resource\Collection
{
$permissions = $user->allPermissions();
return $this->collection($permissions, $this->getPermissionTransformer());
}
public function includeIntegrations(User $user): Item
{
return $this->item($user, $this->getIntegrationsTransformer());
}
private function getTeamTransformer(): TransformerAbstract
{
return $this->container->get(TeamTransformer::class);
}
private function getGroupTransformer(): GroupTransformer
{
return $this->container->get(GroupTransformer::class);
}
private function getIntegrationsTransformer(): IntegrationTransformer
{
return $this->container->get(IntegrationTransformer::class);
}
private function getPermissionTransformer(): PermissionTransformer
{
return $this->container->get(PermissionTransformer::class);
}
private function getRoleTransformer(): RoleTransformer
{
return $this->container->get(RoleTransformer::class);
}
private function getJobTitleTransformer(): JobTitleTransformer
{
return $this->container->get(JobTitleTransformer::class);
}
private function getSidekickService(): SidekickService
{
/** @var SidekickService */
return $this->container->get(SidekickService::class);
}
private function getUserService(): UserService
{
/** @var UserService */
return $this->container->get(UserService::class);
}
private function getAutomatedReportsRepository(): AutomatedReportsRepository
{
/** @var AutomatedReportsRepository */
return $this->container->get(AutomatedReportsRepository::class);
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Component\DealInsights;
use Doctrine\DBAL\Connection;
use Generator;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Jiminny\Component\DealInsights\Forecast\DealData;
use Jiminny\Component\DealInsights\Forecast\DealsFilter;
use Jiminny\Component\DealInsights\QueryBuilder\QueryBuilder;
use Jiminny\Component\DealInsights\QueryBuilder\Visitor\QueryBuilderVisitorInterface;
use Jiminny\Contracts\Services\Crm\ServiceInterface;
use Jiminny\Exceptions\SocialAccountTokenInvalidException;
use Jiminny\Models\Activity;
use Jiminny\Models\Crm\Configuration;
use Jiminny\Models\Crm\Field;
use Jiminny\Models\Stage;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Models;
use Jiminny\Services\Crm\IntegrationApp\DTO\Utils\UrlGeneratorInterface;
use Jiminny\Services\Crm\ProviderRegistry;
use Jiminny\Traits\RequiresUUID;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
class DealsRepository implements DealsRepositoryInterface
{
private Connection $connection;
private ProviderRegistry $providerRegistry;
/**
* @var QueryBuilderVisitorInterface[]
*/
private array $visitors = [];
/**
* @param QueryBuilderVisitorInterface[] $visitors
*/
public function __construct(Connection $connection, ProviderRegistry $crmProviderRegistry, array $visitors = [])
{
$this->connection = $connection;
$this->providerRegistry = $crmProviderRegistry;
foreach ($visitors as $visitor) {
$this->visitors[$visitor->getIdentifier()] = $visitor;
}
}
public function getDeals(CriteriaInterface $criteria): array
{
$context = $criteria->getContext();
$team = $context->getTeam();
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$this->visit($qb, $criteria);
return $this->execute($team, $crmService, $qb);
}
public function getDeal(Team $team, int $id): array
{
$crmService = $this->getCrmService($team);
$qb = $this->createQueryBuilder(QueryBuilder::REALM_DEALS);
$qb = $this->getSearchSelectAndWhereClauses($qb);
$qb->andWhere('opp.id = :id')->setParameter('id', $id);
return $this->execute($team, $crmService, $qb);
}
public function getCrmFieldData(array $crmFields, int $crmId, array $opportunityIds = [])
{
$qb = new QueryBuilder($this->connection);
$qb
->select('f.id', 'f.crm_provider_id AS field_name', 'f.label', 'fd.object_id AS dealId', 'fd.value')
->from('crm_fields', 'f')
->join('f', 'crm_field_data', 'fd', 'fd.crm_field_id = f.id')
->where('f.crm_configuration_id = :crm')
->andWhere('f.object_type = :type')
->andWhere('fd.object_id IN (' . implode(',', $opportunityIds) . ')')
->orderBy('fd.object_id', 'ASC')
->addOrderBy('fd.updated_at', 'ASC')
->setParameter('type', Field::OBJECT_OPPORTUNITY)
->setParameter('crm', $crmId)
;
if (! empty($crmFields)) {
$fields = array_map(fn ($value): string => '"' . $value . '"', $crmFields);
$qb->andWhere('f.crm_provider_id IN (' . implode(',', $fields) . ')');
}
return $qb->executeQuery()->fetchAllAssociative();
}
public function getTotalsInDefaultCurrency(CriteriaInterface $criteria): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAssociative();
}
public function getTotals(CriteriaInterface $criteria, string $defaultCurrency): array
{
$qb = $this->createQueryBuilder(QueryBuilder::REALM_TOTALS);
$qb
->select('COALESCE(opp.currency_code, "' . $defaultCurrency . '") AS currency')
->addSelect('SUM(opp.value) as total')
->addSelect('count(*) as `count`')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not include deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'))
->groupBy('currency')
;
$this->visit($qb, $criteria);
return $qb->executeQuery()->fetchAllAssociative();
}
public function getDealActivities(CriteriaInterface $criteria): array
{
$qb = Activity::with(['participants', 'user'])
->where('opportunity_id', $criteria->getOpportunityId())
->whereDate('actual_start_time', '>=', $criteria->getPeriod()->getStartDate())
->whereDate('actual_start_time', '<=', $criteria->getPeriod()->getEndDate())
->orderBy($criteria->getSortBy(), $criteria->getSortDirection())
;
// Should we filter activities by criteria? It's intended to filter deals.
return $qb->get()->all();
}
public function getStages(CriteriaInterface $criteria): array
{
$qb = new QueryBuilder($this->connection);
$qb
->select('id', 'label', 'sequence')
->from('stages', 's')
->where('crm_configuration_id = :crm_configuration_id')
->andWhere('type = :type')
->orderBy('sequence', 'ASC')
->setParameter('crm_configuration_id', $criteria->getContext()->getTeam()->getCrmConfiguration()->getId())
->setParameter('type', Stage::TYPE_OPPORTUNITY);
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$result[$row['id']] = [
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
public function getConfigurationStages(Configuration $configuration): Collection
{
return $configuration
->stages()
->where('type', Stage::TYPE_OPPORTUNITY)
->get();
}
public function getPipelineData(Configuration $crm): array
{
$qb = new QueryBuilder($this->connection);
$provider = $crm->provider;
$qb
->select('s.label', 's.crm_provider_id', 's.sequence', 'bps.business_process_id AS pipeline_id')
->from('stages', 's')
->join('s', 'business_process_stages', 'bps', 's.id=bps.stage_id')
->where('s.crm_configuration_id = :crm_configuration_id')
->andWhere('s.type = :type')
->orderBy('bps.business_process_id', 'ASC')
->addOrderBy('s.sequence', 'ASC')
->setParameter('crm_configuration_id', $crm->id)
->setParameter('type', Stage::TYPE_OPPORTUNITY)
;
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$value = $provider === Configuration::PROVIDER_SALESFORCE ? $row['label'] : $row['crm_provider_id'];
$result[$row['pipeline_id']][] = [
'value' => $value,
'label' => $row['label'],
'sequence' => $row['sequence'],
];
}
return $result;
}
private function createQueryBuilder(string $realm): QueryBuilder
{
return (new QueryBuilder($this->connection))
->setRealm($realm)
->from('opportunities', 'opp')
->leftJoin('opp', 'record_types', 'rt', 'opp.record_type_id = rt.id')
->leftJoin('opp', 'users', 'usr', 'opp.user_id = usr.id')
->leftJoin('opp', 'accounts', 'acc', 'opp.account_id = acc.id')
;
}
/**
* Applies all applicable visitors and returns the IDs of the executed ones
*
* @return string[]
*/
private function visit(QueryBuilder $queryBuilder, CriteriaInterface $criteria): array
{
$queryVisitors = [];
foreach ($this->visitors as $visitor) {
if ($visitor->isSatisfiedBy($criteria, $queryBuilder->getRealm())) {
$visitor->visit($queryBuilder, $criteria);
$queryVisitors[] = $visitor->getIdentifier();
}
}
return $queryVisitors;
}
private function hydrateStages(array $deals): array
{
foreach ($this->fetchStages(array_keys($deals)) as $stage) {
$oppId = (int) $stage['opportunity_id'];
if (! isset($deals[$oppId])) {
continue; // or throw??!
}
$deals[$oppId]['stages'][] = [
'id' => $stage['stage_id'],
'name' => $stage['label'],
'enteredAt' => $stage['created_at'],
];
}
return $deals;
}
/**
* @param int[] $dealIds
*/
private function fetchStages(array $dealIds): array
{
if (empty($dealIds)) {
return [];
}
$qb = new QueryBuilder($this->connection);
$qb
->select('os.opportunity_id', 's.id AS stage_id', 's.label', 's.created_at')
->from('opportunity_stages', 'os')
->leftJoin('os', 'stages', 's', 'os.stage_id=s.id')
->where($qb->expr()->in('os.opportunity_id', $dealIds))
->orderBy('os.opportunity_id', 'ASC')
->addOrderBy('s.created_at', 'ASC')
;
return $qb->executeQuery()->fetchAllAssociative();
}
private function execute(Team $team, ServiceInterface $crmService, QueryBuilder $qb): array
{
$result = [];
foreach ($qb->executeQuery()->fetchAllAssociative() as $row) {
$data = [
'uuid' => RequiresUUID::toNormal($row['uuid']),
'name' => $row['name'],
'url' => $crmService->generateProviderUrl($row['opp_provider_id'], 'opportunity'),
'account' => [
'name' => $row['acc_name'],
'url' => $crmService->generateProviderUrl(
providerId: $row['acc_provider_id'],
objectType: $row['acc_is_internal'] ? 'internal-account' : 'account'
),
],
'owner' => null,
'rawValue' => [
'amount' => (float) $row['value'],
'currency' => $row['currency_code'],
],
'value' => formatOpportunityValue((float) $row['value'], $row['currency_code']),
'openDate' => $row['remotely_created_at'] ?? null,
'closeDate' => $row['close_date'] ?? null,
'stages' => [],
'currentPipelineId' => $row['pipeline_id'],
'currentStage' => [
'id' => $row['stage_id'],
'enteredAt' => $row['stage_updated_at'],
],
'currentStageUpdatedAt' => $row['stage_updated_at'],
'isClosed' => (bool) $row['is_closed'],
'isWon' => (bool) $row['is_won'],
];
if (isset($row['owner_uuid'])) {
$data['owner'] = [
'uuid' => RequiresUUID::toNormal($row['owner_uuid']),
'name' => $row['owner_name'],
'photoUrl' => $row['owner_photo'] === null
? null
: client_cdn($row['owner_photo'], $team),
'id' => $row['owner_id'],
'job' => $row['owner_job'],
];
}
$result[(int) $row['opp_id']] = $data;
}
return $this->hydrateStages($result);
}
private function getSearchSelectAndWhereClauses(QueryBuilder $queryBuilder): QueryBuilder
{
$qb = clone $queryBuilder;
$qb->leftJoin('usr', 'job_titles', 'jt', 'usr.job_title_id = jt.id');
$qb
->select(...[
'opp.id as opp_id',
'opp.uuid',
'opp.name',
'opp.value',
'opp.currency_code',
'opp.close_date',
'opp.remotely_created_at',
'opp.is_closed',
'opp.is_won',
])
->addSelect(...[
'usr.uuid as owner_uuid',
'usr.name AS owner_name',
'usr.photo_path as owner_photo',
'usr.id AS owner_id',
'jt.name as owner_job',
])
->addSelect('opp.stage_id', 'opp.stage_updated_at')
->addSelect(...[
'acc.name AS acc_name',
'acc.is_internal as acc_is_internal',
'opp.stage_updated_at',
'acc.crm_provider_id AS acc_provider_id',
'opp.crm_provider_id AS opp_provider_id',
])
->addSelect('rt.business_process_id AS pipeline_id')
->where($qb->expr()->isNotNull('opp.user_id')) // we should not display deals owned by external users
->andWhere($qb->expr()->isNull('opp.deleted_at'));
return $qb;
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws SocialAccountTokenInvalidException
*/
private function getCrmService(Team $team): ServiceInterface
{
$crmService = $this->providerRegistry->get($team->crm->provider);
$crmService->setConfiguration($team->crm);
if ($crmService instanceof UrlGeneratorInterface) {
$crmService->setCrmUrlGenerator($team->crm);
}
return $crmService;
}
/**
*
* @return Generator<DealData>
*/
public function getForecastData(DealsFilter $filter): Generator
{
$opportunities = DB::query()
->select([
'o.value',
'o.close_date',
'o.currency_code',
'o.is_won',
'o.is_closed',
'o.probability',
'o.forecast_category',
])
->from('opportunities', 'o')
->join('users', 'users.id', '=', 'o.user_id')
->join('groups', 'groups.id', '=', 'users.group_id')
->where('users.team_id', $filter->getTeam()->getId())
->where('o.close_date', '>=', $filter->getStartDate())
->where('o.close_date', '<=', $filter->getEndDate())
->where('o.currency_code', $filter->getCurrency())
->where('o.deleted_at', '=', null)
;
$userUuidList = $filter->getUserUuidList();
if (! empty($userUuidList)) {
$userUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $userUuidList);
$opportunities->whereIn('users.uuid', $userUuidList);
}
$groupUuidList = $filter->getGroupUuidList();
if (! empty($groupUuidList)) {
$groupUuidList = array_map(fn ($uuid) => RequiresUUID::toOptimized($uuid), $groupUuidList);
$opportunities->whereIn('groups.uuid', $groupUuidList);
}
foreach ($opportunities->cursor() as $row) {
yield new DealData(
(float) $row->value,
$row->close_date,
! empty($row->is_won),
! empty($row->is_closed),
$row->probability ?: 0,
$row->forecast_category ?: '',
);
}
}
public function getUserOpportunitySubscriptions(User $user, array $opportunityIds): Collection
{
return $user->subscriptionSets()
->where(static function (Eloquent\Builder $query): void {
$query
->whereNull('expired_at')
->orWhere('expired_at', '>=', now());
})
->join('activity_subscriptions', function (Builder $join) use ($opportunityIds) {
$join
->on('subscription_set_id', '=', 'activity_subscription_sets.id');
$join
->where('followable_type', Models\Activity\Subscription::FOLLOWABLE_TYPE_OPPORTUNITY)
->whereIn('followable_id', $opportunityIds);
})
->pluck('followable_id');
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app
.circleci
.cursor
.github
.sonarlint
.vscode
.windsurf
app, sources root
Actions
Component
Acl
ActionItems
Activity
ActivityAnalytics
ActivitySearch
AiActivityType
AiAutomation
AiCallScoring
AskAnything
Dtos
Events
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi
AWS
BillingManagement
Cache
CoachingFeedback
Country
CustomerApi
Database
Datadog
DateTime
DealInsights
Activity
ActivityAggregator.php, class
ActivityAggregatorInterface.php, interface
DatabaseActivities.php, class
DatasourceInterface.php, interface
RelatedActivity.php, class
RelatedActivityInterface.php, interface
Commands
Comments
Forecast
Jobs
QueryBuilder
Services
ClosingPeriodOptionDecorator.php, class
CreatedPeriodOptionDecorator.php, class
Criteria.php, class
CriteriaInterface.php, interface
CriteriaNormalizer.php, class
CrmService.php, class
CrmServiceInterface.php, interface
DealContactService.php, class
DealInsightsCriteriaBuilder.php, class
DealService.php, class
DealServiceInterface.php, interface
DealsRepository.php, class
DealsRepositoryInterface.php, interface...
|
NULL
|
|
76291
|
1909
|
16
|
2026-04-24T07:40:05.825611+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016405825_m1.jpg...
|
PhpStorm
|
faVsco.js – SendReportNotGeneratedMailJobTest.php
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
SendReportNotGeneratedMailJobTes…edFormat
Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Jobs\AutomatedReports;
use Exception;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
use Jiminny\Jobs\AutomatedReports\SendReportNotGeneratedMailJob;
use Jiminny\Mail\Reports\ReportNotGenerated;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Tests\TestCase;
class SendReportNotGeneratedMailJobTest extends TestCase
{
private LoggerInterface&MockObject $logger;
private SendReportNotGeneratedMailJob $job;
private string $reportUuid = 'test-uuid';
private string $recipientEmail;
private string $recipientName;
private string $reportName;
private string $periodName;
private string $reportsPageUrl;
protected function setUp(): void
{
parent::setUp();
$this->recipientEmail = '[EMAIL]';
$this->recipientName = 'Test User';
$this->reportName = 'Exec Summary';
$this->periodName = 'Q3 2025';
$this->reportsPageUrl = '[URL_WITH_CREDENTIALS] array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"SendReportNotGeneratedMailJobTes…edFormat","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Jobs\\AutomatedReports;\n\nuse Exception;\nuse Illuminate\\Support\\Facades\\Config;\nuse Illuminate\\Support\\Facades\\Mail;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportNotGeneratedMailJob;\nuse Jiminny\\Mail\\Reports\\ReportNotGenerated;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Psr\\Log\\LoggerInterface;\nuse Tests\\TestCase;\n\nclass SendReportNotGeneratedMailJobTest extends TestCase\n{\n private LoggerInterface&MockObject $logger;\n private SendReportNotGeneratedMailJob $job;\n private string $reportUuid = 'test-uuid';\n private string $recipientEmail;\n private string $recipientName;\n private string $reportName;\n private string $periodName;\n private string $reportsPageUrl;\n\n protected function setUp(): void\n {\n parent::setUp();\n\n $this->recipientEmail = 'user@example.com';\n $this->recipientName = 'Test User';\n $this->reportName = 'Exec Summary';\n $this->periodName = 'Q3 2025';\n $this->reportsPageUrl = 'https://app.jiminny.com/reports';\n\n $this->logger = $this->createMock(LoggerInterface::class);\n\n Config::set('app.env', 'testing');\n\n Mail::fake();\n\n $this->job = new SendReportNotGeneratedMailJob(\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl\n );\n }\n\n public function testUniqueIdReturnsExpectedFormat(): void\n {\n $expected = $this->reportUuid . '_not_generated_' . $this->recipientEmail;\n $this->assertEquals($expected, $this->job->uniqueId());\n }\n\n public function testHandleSendsEmailSuccessfully(): void\n {\n Config::set('mail.from.address', 'noreply@jiminny.com');\n Config::set('mail.from.name', 'Jiminny');\n Config::set('jiminny.deploy_region', 'us');\n Config::set('logos.cdn.header', 'https://cdn.test.com/header.png');\n Config::set('logos.cdn.footer', 'https://cdn.test.com/footer.png');\n\n $this->logger->expects($this->once())\n ->method('info')\n ->with('[Send Report Not Generated Mail] Email sent', [\n 'uuid' => $this->reportUuid,\n 'email' => $this->recipientEmail,\n 'recipientName' => $this->recipientName,\n ]);\n\n $this->job->handle($this->logger);\n\n Mail::assertQueued(ReportNotGenerated::class, function ($mail) {\n return $mail->hasTo($this->recipientEmail);\n });\n }\n\n public function testHandleLogsErrorAndRetriesOnException(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error')\n ->with('[Send Report Not Generated Mail] Error sending email', $this->callback(function ($context) {\n return $context['uuid'] === $this->reportUuid\n && $context['email'] === $this->recipientEmail\n && $context['error'] === 'SMTP error'\n && isset($context['trace']);\n }));\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['release'])\n ->getMock();\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(60);\n\n $jobMock->handle($this->logger);\n }\n\n public function testHandleFailsJobAfterMaxAttempts(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'fail'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(3);\n\n $jobMock->expects($this->once())\n ->method('fail')\n ->with($this->isInstanceOf(Exception::class));\n\n $jobMock->handle($this->logger);\n }\n\n public function testJobPropertiesAreSetCorrectly(): void\n {\n $this->assertEquals(3, $this->job->tries);\n $this->assertEquals([60, 300, 600], $this->job->backoff);\n $this->assertEquals(60, $this->job->timeout);\n }\n\n public function testHandleUsesCorrectBackoffForSecondAttempt(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'release'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(2);\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(300);\n\n $jobMock->handle($this->logger);\n }\n\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Jobs\\AutomatedReports;\n\nuse Exception;\nuse Illuminate\\Support\\Facades\\Config;\nuse Illuminate\\Support\\Facades\\Mail;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportNotGeneratedMailJob;\nuse Jiminny\\Mail\\Reports\\ReportNotGenerated;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Psr\\Log\\LoggerInterface;\nuse Tests\\TestCase;\n\nclass SendReportNotGeneratedMailJobTest extends TestCase\n{\n private LoggerInterface&MockObject $logger;\n private SendReportNotGeneratedMailJob $job;\n private string $reportUuid = 'test-uuid';\n private string $recipientEmail;\n private string $recipientName;\n private string $reportName;\n private string $periodName;\n private string $reportsPageUrl;\n\n protected function setUp(): void\n {\n parent::setUp();\n\n $this->recipientEmail = 'user@example.com';\n $this->recipientName = 'Test User';\n $this->reportName = 'Exec Summary';\n $this->periodName = 'Q3 2025';\n $this->reportsPageUrl = 'https://app.jiminny.com/reports';\n\n $this->logger = $this->createMock(LoggerInterface::class);\n\n Config::set('app.env', 'testing');\n\n Mail::fake();\n\n $this->job = new SendReportNotGeneratedMailJob(\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl\n );\n }\n\n public function testUniqueIdReturnsExpectedFormat(): void\n {\n $expected = $this->reportUuid . '_not_generated_' . $this->recipientEmail;\n $this->assertEquals($expected, $this->job->uniqueId());\n }\n\n public function testHandleSendsEmailSuccessfully(): void\n {\n Config::set('mail.from.address', 'noreply@jiminny.com');\n Config::set('mail.from.name', 'Jiminny');\n Config::set('jiminny.deploy_region', 'us');\n Config::set('logos.cdn.header', 'https://cdn.test.com/header.png');\n Config::set('logos.cdn.footer', 'https://cdn.test.com/footer.png');\n\n $this->logger->expects($this->once())\n ->method('info')\n ->with('[Send Report Not Generated Mail] Email sent', [\n 'uuid' => $this->reportUuid,\n 'email' => $this->recipientEmail,\n 'recipientName' => $this->recipientName,\n ]);\n\n $this->job->handle($this->logger);\n\n Mail::assertQueued(ReportNotGenerated::class, function ($mail) {\n return $mail->hasTo($this->recipientEmail);\n });\n }\n\n public function testHandleLogsErrorAndRetriesOnException(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error')\n ->with('[Send Report Not Generated Mail] Error sending email', $this->callback(function ($context) {\n return $context['uuid'] === $this->reportUuid\n && $context['email'] === $this->recipientEmail\n && $context['error'] === 'SMTP error'\n && isset($context['trace']);\n }));\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['release'])\n ->getMock();\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(60);\n\n $jobMock->handle($this->logger);\n }\n\n public function testHandleFailsJobAfterMaxAttempts(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'fail'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(3);\n\n $jobMock->expects($this->once())\n ->method('fail')\n ->with($this->isInstanceOf(Exception::class));\n\n $jobMock->handle($this->logger);\n }\n\n public function testJobPropertiesAreSetCorrectly(): void\n {\n $this->assertEquals(3, $this->job->tries);\n $this->assertEquals([60, 300, 600], $this->job->backoff);\n $this->assertEquals(60, $this->job->timeout);\n }\n\n public function testHandleUsesCorrectBackoffForSecondAttempt(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'release'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(2);\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(300);\n\n $jobMock->handle($this->logger);\n }\n\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-1001287872104142188
|
8405659761666407192
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
SendReportNotGeneratedMailJobTes…edFormat
Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Jobs\AutomatedReports;
use Exception;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
use Jiminny\Jobs\AutomatedReports\SendReportNotGeneratedMailJob;
use Jiminny\Mail\Reports\ReportNotGenerated;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Tests\TestCase;
class SendReportNotGeneratedMailJobTest extends TestCase
{
private LoggerInterface&MockObject $logger;
private SendReportNotGeneratedMailJob $job;
private string $reportUuid = 'test-uuid';
private string $recipientEmail;
private string $recipientName;
private string $reportName;
private string $periodName;
private string $reportsPageUrl;
protected function setUp(): void
{
parent::setUp();
$this->recipientEmail = '[EMAIL]';
$this->recipientName = 'Test User';
$this->reportName = 'Exec Summary';
$this->periodName = 'Q3 2025';
$this->reportsPageUrl = '[URL_WITH_CREDENTIALS] array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
76292
|
1910
|
15
|
2026-04-24T07:40:05.716376+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016405716_m2.jpg...
|
PhpStorm
|
faVsco.js – SendReportNotGeneratedMailJobTest.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
SendReportNotGeneratedMailJobTes…edFormat
Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Jobs\AutomatedReports;
use Exception;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
use Jiminny\Jobs\AutomatedReports\SendReportNotGeneratedMailJob;
use Jiminny\Mail\Reports\ReportNotGenerated;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Tests\TestCase;
class SendReportNotGeneratedMailJobTest extends TestCase
{
private LoggerInterface&MockObject $logger;
private SendReportNotGeneratedMailJob $job;
private string $reportUuid = 'test-uuid';
private string $recipientEmail;
private string $recipientName;
private string $reportName;
private string $periodName;
private string $reportsPageUrl;
protected function setUp(): void
{
parent::setUp();
$this->recipientEmail = '[EMAIL]';
$this->recipientName = 'Test User';
$this->reportName = 'Exec Summary';
$this->periodName = 'Q3 2025';
$this->reportsPageUrl = '[URL_WITH_CREDENTIALS] array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.89944136,"width":0.100398935,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"bounds":{"left":0.8753325,"top":0.915403,"width":0.11037234,"height":0.027134877},"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.915403,"width":0.09507979,"height":0.027134877},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.78789896,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"SendReportNotGeneratedMailJobTes…edFormat","depth":6,"bounds":{"left":0.8031915,"top":0.019952115,"width":0.11236702,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.58610374,"top":0.32322428,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.59607714,"top":0.32322428,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6050532,"top":0.3216281,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.61236703,"top":0.3216281,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Jobs\\AutomatedReports;\n\nuse Exception;\nuse Illuminate\\Support\\Facades\\Config;\nuse Illuminate\\Support\\Facades\\Mail;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportNotGeneratedMailJob;\nuse Jiminny\\Mail\\Reports\\ReportNotGenerated;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Psr\\Log\\LoggerInterface;\nuse Tests\\TestCase;\n\nclass SendReportNotGeneratedMailJobTest extends TestCase\n{\n private LoggerInterface&MockObject $logger;\n private SendReportNotGeneratedMailJob $job;\n private string $reportUuid = 'test-uuid';\n private string $recipientEmail;\n private string $recipientName;\n private string $reportName;\n private string $periodName;\n private string $reportsPageUrl;\n\n protected function setUp(): void\n {\n parent::setUp();\n\n $this->recipientEmail = 'user@example.com';\n $this->recipientName = 'Test User';\n $this->reportName = 'Exec Summary';\n $this->periodName = 'Q3 2025';\n $this->reportsPageUrl = 'https://app.jiminny.com/reports';\n\n $this->logger = $this->createMock(LoggerInterface::class);\n\n Config::set('app.env', 'testing');\n\n Mail::fake();\n\n $this->job = new SendReportNotGeneratedMailJob(\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl\n );\n }\n\n public function testUniqueIdReturnsExpectedFormat(): void\n {\n $expected = $this->reportUuid . '_not_generated_' . $this->recipientEmail;\n $this->assertEquals($expected, $this->job->uniqueId());\n }\n\n public function testHandleSendsEmailSuccessfully(): void\n {\n Config::set('mail.from.address', 'noreply@jiminny.com');\n Config::set('mail.from.name', 'Jiminny');\n Config::set('jiminny.deploy_region', 'us');\n Config::set('logos.cdn.header', 'https://cdn.test.com/header.png');\n Config::set('logos.cdn.footer', 'https://cdn.test.com/footer.png');\n\n $this->logger->expects($this->once())\n ->method('info')\n ->with('[Send Report Not Generated Mail] Email sent', [\n 'uuid' => $this->reportUuid,\n 'email' => $this->recipientEmail,\n 'recipientName' => $this->recipientName,\n ]);\n\n $this->job->handle($this->logger);\n\n Mail::assertQueued(ReportNotGenerated::class, function ($mail) {\n return $mail->hasTo($this->recipientEmail);\n });\n }\n\n public function testHandleLogsErrorAndRetriesOnException(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error')\n ->with('[Send Report Not Generated Mail] Error sending email', $this->callback(function ($context) {\n return $context['uuid'] === $this->reportUuid\n && $context['email'] === $this->recipientEmail\n && $context['error'] === 'SMTP error'\n && isset($context['trace']);\n }));\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['release'])\n ->getMock();\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(60);\n\n $jobMock->handle($this->logger);\n }\n\n public function testHandleFailsJobAfterMaxAttempts(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'fail'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(3);\n\n $jobMock->expects($this->once())\n ->method('fail')\n ->with($this->isInstanceOf(Exception::class));\n\n $jobMock->handle($this->logger);\n }\n\n public function testJobPropertiesAreSetCorrectly(): void\n {\n $this->assertEquals(3, $this->job->tries);\n $this->assertEquals([60, 300, 600], $this->job->backoff);\n $this->assertEquals(60, $this->job->timeout);\n }\n\n public function testHandleUsesCorrectBackoffForSecondAttempt(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'release'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(2);\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(300);\n\n $jobMock->handle($this->logger);\n }\n\n}","depth":4,"bounds":{"left":0.3799867,"top":0.3200319,"width":0.2393617,"height":0.6799681},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Jobs\\AutomatedReports;\n\nuse Exception;\nuse Illuminate\\Support\\Facades\\Config;\nuse Illuminate\\Support\\Facades\\Mail;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportNotGeneratedMailJob;\nuse Jiminny\\Mail\\Reports\\ReportNotGenerated;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Psr\\Log\\LoggerInterface;\nuse Tests\\TestCase;\n\nclass SendReportNotGeneratedMailJobTest extends TestCase\n{\n private LoggerInterface&MockObject $logger;\n private SendReportNotGeneratedMailJob $job;\n private string $reportUuid = 'test-uuid';\n private string $recipientEmail;\n private string $recipientName;\n private string $reportName;\n private string $periodName;\n private string $reportsPageUrl;\n\n protected function setUp(): void\n {\n parent::setUp();\n\n $this->recipientEmail = 'user@example.com';\n $this->recipientName = 'Test User';\n $this->reportName = 'Exec Summary';\n $this->periodName = 'Q3 2025';\n $this->reportsPageUrl = 'https://app.jiminny.com/reports';\n\n $this->logger = $this->createMock(LoggerInterface::class);\n\n Config::set('app.env', 'testing');\n\n Mail::fake();\n\n $this->job = new SendReportNotGeneratedMailJob(\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl\n );\n }\n\n public function testUniqueIdReturnsExpectedFormat(): void\n {\n $expected = $this->reportUuid . '_not_generated_' . $this->recipientEmail;\n $this->assertEquals($expected, $this->job->uniqueId());\n }\n\n public function testHandleSendsEmailSuccessfully(): void\n {\n Config::set('mail.from.address', 'noreply@jiminny.com');\n Config::set('mail.from.name', 'Jiminny');\n Config::set('jiminny.deploy_region', 'us');\n Config::set('logos.cdn.header', 'https://cdn.test.com/header.png');\n Config::set('logos.cdn.footer', 'https://cdn.test.com/footer.png');\n\n $this->logger->expects($this->once())\n ->method('info')\n ->with('[Send Report Not Generated Mail] Email sent', [\n 'uuid' => $this->reportUuid,\n 'email' => $this->recipientEmail,\n 'recipientName' => $this->recipientName,\n ]);\n\n $this->job->handle($this->logger);\n\n Mail::assertQueued(ReportNotGenerated::class, function ($mail) {\n return $mail->hasTo($this->recipientEmail);\n });\n }\n\n public function testHandleLogsErrorAndRetriesOnException(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error')\n ->with('[Send Report Not Generated Mail] Error sending email', $this->callback(function ($context) {\n return $context['uuid'] === $this->reportUuid\n && $context['email'] === $this->recipientEmail\n && $context['error'] === 'SMTP error'\n && isset($context['trace']);\n }));\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['release'])\n ->getMock();\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(60);\n\n $jobMock->handle($this->logger);\n }\n\n public function testHandleFailsJobAfterMaxAttempts(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'fail'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(3);\n\n $jobMock->expects($this->once())\n ->method('fail')\n ->with($this->isInstanceOf(Exception::class));\n\n $jobMock->handle($this->logger);\n }\n\n public function testJobPropertiesAreSetCorrectly(): void\n {\n $this->assertEquals(3, $this->job->tries);\n $this->assertEquals([60, 300, 600], $this->job->backoff);\n $this->assertEquals(60, $this->job->timeout);\n }\n\n public function testHandleUsesCorrectBackoffForSecondAttempt(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'release'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(2);\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(300);\n\n $jobMock->handle($this->logger);\n }\n\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-1001287872104142188
|
8405659761666407192
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
SendReportNotGeneratedMailJobTes…edFormat
Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Jobs\AutomatedReports;
use Exception;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
use Jiminny\Jobs\AutomatedReports\SendReportNotGeneratedMailJob;
use Jiminny\Mail\Reports\ReportNotGenerated;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Tests\TestCase;
class SendReportNotGeneratedMailJobTest extends TestCase
{
private LoggerInterface&MockObject $logger;
private SendReportNotGeneratedMailJob $job;
private string $reportUuid = 'test-uuid';
private string $recipientEmail;
private string $recipientName;
private string $reportName;
private string $periodName;
private string $reportsPageUrl;
protected function setUp(): void
{
parent::setUp();
$this->recipientEmail = '[EMAIL]';
$this->recipientName = 'Test User';
$this->reportName = 'Exec Summary';
$this->periodName = 'Q3 2025';
$this->reportsPageUrl = '[URL_WITH_CREDENTIALS] array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
76289
|
|
76286
|
1909
|
13
|
2026-04-24T07:39:55.012525+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016395012_m1.jpg...
|
PhpStorm
|
faVsco.js – SendReportNotGeneratedMailJobTest.php
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
SendReportNotGeneratedMailJobTes…edFormat
Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
3
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Jobs\AutomatedReports;
use Exception;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
use Jiminny\Jobs\AutomatedReports\SendReportNotGeneratedMailJob;
use Jiminny\Mail\Reports\ReportNotGenerated;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Tests\TestCase;
class SendReportNotGeneratedMailJobTest extends TestCase
{
private LoggerInterface&MockObject $logger;
private SendReportNotGeneratedMailJob $job;
private string $reportUuid = 'test-uuid';
private string $recipientEmail;
private string $recipientName;
private string $reportName;
private string $periodName;
private string $reportsPageUrl;
protected function setUp(): void
{
parent::setUp();
$this->recipientEmail = '[EMAIL]';
$this->recipientName = 'Test User';
$this->reportName = 'Exec Summary';
$this->periodName = 'Q3 2025';
$this->reportsPageUrl = '[URL_WITH_CREDENTIALS] array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"SendReportNotGeneratedMailJobTes…edFormat","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Jobs\\AutomatedReports;\n\nuse Exception;\nuse Illuminate\\Support\\Facades\\Config;\nuse Illuminate\\Support\\Facades\\Mail;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportNotGeneratedMailJob;\nuse Jiminny\\Mail\\Reports\\ReportNotGenerated;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Psr\\Log\\LoggerInterface;\nuse Tests\\TestCase;\n\nclass SendReportNotGeneratedMailJobTest extends TestCase\n{\n private LoggerInterface&MockObject $logger;\n private SendReportNotGeneratedMailJob $job;\n private string $reportUuid = 'test-uuid';\n private string $recipientEmail;\n private string $recipientName;\n private string $reportName;\n private string $periodName;\n private string $reportsPageUrl;\n\n protected function setUp(): void\n {\n parent::setUp();\n\n $this->recipientEmail = 'user@example.com';\n $this->recipientName = 'Test User';\n $this->reportName = 'Exec Summary';\n $this->periodName = 'Q3 2025';\n $this->reportsPageUrl = 'https://app.jiminny.com/reports';\n\n $this->logger = $this->createMock(LoggerInterface::class);\n\n Config::set('app.env', 'testing');\n\n Mail::fake();\n\n $this->job = new SendReportNotGeneratedMailJob(\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl\n );\n }\n\n public function testUniqueIdReturnsExpectedFormat(): void\n {\n $expected = $this->reportUuid . '_not_generated_' . $this->recipientEmail;\n $this->assertEquals($expected, $this->job->uniqueId());\n }\n\n public function testHandleSendsEmailSuccessfully(): void\n {\n Config::set('mail.from.address', 'noreply@jiminny.com');\n Config::set('mail.from.name', 'Jiminny');\n Config::set('jiminny.deploy_region', 'us');\n Config::set('logos.cdn.header', 'https://cdn.test.com/header.png');\n Config::set('logos.cdn.footer', 'https://cdn.test.com/footer.png');\n\n $this->logger->expects($this->once())\n ->method('info')\n ->with('[Send Report Not Generated Mail] Email sent', [\n 'uuid' => $this->reportUuid,\n 'email' => $this->recipientEmail,\n 'recipientName' => $this->recipientName,\n ]);\n\n $this->job->handle($this->logger);\n\n Mail::assertQueued(ReportNotGenerated::class, function ($mail) {\n return $mail->hasTo($this->recipientEmail);\n });\n }\n\n public function testHandleLogsErrorAndRetriesOnException(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error')\n ->with('[Send Report Not Generated Mail] Error sending email', $this->callback(function ($context) {\n return $context['uuid'] === $this->reportUuid\n && $context['email'] === $this->recipientEmail\n && $context['error'] === 'SMTP error'\n && isset($context['trace']);\n }));\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['release'])\n ->getMock();\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(60);\n\n $jobMock->handle($this->logger);\n }\n\n public function testHandleFailsJobAfterMaxAttempts(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'fail'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(3);\n\n $jobMock->expects($this->once())\n ->method('fail')\n ->with($this->isInstanceOf(Exception::class));\n\n $jobMock->handle($this->logger);\n }\n\n public function testJobPropertiesAreSetCorrectly(): void\n {\n $this->assertEquals(3, $this->job->tries);\n $this->assertEquals([60, 300, 600], $this->job->backoff);\n $this->assertEquals(60, $this->job->timeout);\n }\n\n public function testHandleUsesCorrectBackoffForSecondAttempt(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'release'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(2);\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(300);\n\n $jobMock->handle($this->logger);\n }\n\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Jobs\\AutomatedReports;\n\nuse Exception;\nuse Illuminate\\Support\\Facades\\Config;\nuse Illuminate\\Support\\Facades\\Mail;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportNotGeneratedMailJob;\nuse Jiminny\\Mail\\Reports\\ReportNotGenerated;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Psr\\Log\\LoggerInterface;\nuse Tests\\TestCase;\n\nclass SendReportNotGeneratedMailJobTest extends TestCase\n{\n private LoggerInterface&MockObject $logger;\n private SendReportNotGeneratedMailJob $job;\n private string $reportUuid = 'test-uuid';\n private string $recipientEmail;\n private string $recipientName;\n private string $reportName;\n private string $periodName;\n private string $reportsPageUrl;\n\n protected function setUp(): void\n {\n parent::setUp();\n\n $this->recipientEmail = 'user@example.com';\n $this->recipientName = 'Test User';\n $this->reportName = 'Exec Summary';\n $this->periodName = 'Q3 2025';\n $this->reportsPageUrl = 'https://app.jiminny.com/reports';\n\n $this->logger = $this->createMock(LoggerInterface::class);\n\n Config::set('app.env', 'testing');\n\n Mail::fake();\n\n $this->job = new SendReportNotGeneratedMailJob(\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl\n );\n }\n\n public function testUniqueIdReturnsExpectedFormat(): void\n {\n $expected = $this->reportUuid . '_not_generated_' . $this->recipientEmail;\n $this->assertEquals($expected, $this->job->uniqueId());\n }\n\n public function testHandleSendsEmailSuccessfully(): void\n {\n Config::set('mail.from.address', 'noreply@jiminny.com');\n Config::set('mail.from.name', 'Jiminny');\n Config::set('jiminny.deploy_region', 'us');\n Config::set('logos.cdn.header', 'https://cdn.test.com/header.png');\n Config::set('logos.cdn.footer', 'https://cdn.test.com/footer.png');\n\n $this->logger->expects($this->once())\n ->method('info')\n ->with('[Send Report Not Generated Mail] Email sent', [\n 'uuid' => $this->reportUuid,\n 'email' => $this->recipientEmail,\n 'recipientName' => $this->recipientName,\n ]);\n\n $this->job->handle($this->logger);\n\n Mail::assertQueued(ReportNotGenerated::class, function ($mail) {\n return $mail->hasTo($this->recipientEmail);\n });\n }\n\n public function testHandleLogsErrorAndRetriesOnException(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error')\n ->with('[Send Report Not Generated Mail] Error sending email', $this->callback(function ($context) {\n return $context['uuid'] === $this->reportUuid\n && $context['email'] === $this->recipientEmail\n && $context['error'] === 'SMTP error'\n && isset($context['trace']);\n }));\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['release'])\n ->getMock();\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(60);\n\n $jobMock->handle($this->logger);\n }\n\n public function testHandleFailsJobAfterMaxAttempts(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'fail'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(3);\n\n $jobMock->expects($this->once())\n ->method('fail')\n ->with($this->isInstanceOf(Exception::class));\n\n $jobMock->handle($this->logger);\n }\n\n public function testJobPropertiesAreSetCorrectly(): void\n {\n $this->assertEquals(3, $this->job->tries);\n $this->assertEquals([60, 300, 600], $this->job->backoff);\n $this->assertEquals(60, $this->job->timeout);\n }\n\n public function testHandleUsesCorrectBackoffForSecondAttempt(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'release'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(2);\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(300);\n\n $jobMock->handle($this->logger);\n }\n\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5346950300665258389
|
8405655363615669016
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
SendReportNotGeneratedMailJobTes…edFormat
Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
3
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Jobs\AutomatedReports;
use Exception;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
use Jiminny\Jobs\AutomatedReports\SendReportNotGeneratedMailJob;
use Jiminny\Mail\Reports\ReportNotGenerated;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Tests\TestCase;
class SendReportNotGeneratedMailJobTest extends TestCase
{
private LoggerInterface&MockObject $logger;
private SendReportNotGeneratedMailJob $job;
private string $reportUuid = 'test-uuid';
private string $recipientEmail;
private string $recipientName;
private string $reportName;
private string $periodName;
private string $reportsPageUrl;
protected function setUp(): void
{
parent::setUp();
$this->recipientEmail = '[EMAIL]';
$this->recipientName = 'Test User';
$this->reportName = 'Exec Summary';
$this->periodName = 'Q3 2025';
$this->reportsPageUrl = '[URL_WITH_CREDENTIALS] array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
76284
|
|
76287
|
1910
|
13
|
2026-04-24T07:39:55.012487+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016395012_m2.jpg...
|
PhpStorm
|
faVsco.js – SendReportNotGeneratedMailJobTest.php
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
SendReportNotGeneratedMailJobTes…edFormat
Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
3
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Jobs\AutomatedReports;
use Exception;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
use Jiminny\Jobs\AutomatedReports\SendReportNotGeneratedMailJob;
use Jiminny\Mail\Reports\ReportNotGenerated;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Tests\TestCase;
class SendReportNotGeneratedMailJobTest extends TestCase
{
private LoggerInterface&MockObject $logger;
private SendReportNotGeneratedMailJob $job;
private string $reportUuid = 'test-uuid';
private string $recipientEmail;
private string $recipientName;
private string $reportName;
private string $periodName;
private string $reportsPageUrl;
protected function setUp(): void
{
parent::setUp();
$this->recipientEmail = '[EMAIL]';
$this->recipientName = 'Test User';
$this->reportName = 'Exec Summary';
$this->periodName = 'Q3 2025';
$this->reportsPageUrl = '[URL_WITH_CREDENTIALS] array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.89944136,"width":0.100398935,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"bounds":{"left":0.8753325,"top":0.915403,"width":0.11037234,"height":0.027134877},"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.915403,"width":0.09507979,"height":0.027134877},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.78789896,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"SendReportNotGeneratedMailJobTes…edFormat","depth":6,"bounds":{"left":0.8031915,"top":0.019952115,"width":0.11236702,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"3","depth":4,"bounds":{"left":0.58610374,"top":0.32322428,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"1","depth":4,"bounds":{"left":0.59607714,"top":0.32322428,"width":0.00731383,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6050532,"top":0.3216281,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.61236703,"top":0.3216281,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Jobs\\AutomatedReports;\n\nuse Exception;\nuse Illuminate\\Support\\Facades\\Config;\nuse Illuminate\\Support\\Facades\\Mail;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportNotGeneratedMailJob;\nuse Jiminny\\Mail\\Reports\\ReportNotGenerated;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Psr\\Log\\LoggerInterface;\nuse Tests\\TestCase;\n\nclass SendReportNotGeneratedMailJobTest extends TestCase\n{\n private LoggerInterface&MockObject $logger;\n private SendReportNotGeneratedMailJob $job;\n private string $reportUuid = 'test-uuid';\n private string $recipientEmail;\n private string $recipientName;\n private string $reportName;\n private string $periodName;\n private string $reportsPageUrl;\n\n protected function setUp(): void\n {\n parent::setUp();\n\n $this->recipientEmail = 'user@example.com';\n $this->recipientName = 'Test User';\n $this->reportName = 'Exec Summary';\n $this->periodName = 'Q3 2025';\n $this->reportsPageUrl = 'https://app.jiminny.com/reports';\n\n $this->logger = $this->createMock(LoggerInterface::class);\n\n Config::set('app.env', 'testing');\n\n Mail::fake();\n\n $this->job = new SendReportNotGeneratedMailJob(\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl\n );\n }\n\n public function testUniqueIdReturnsExpectedFormat(): void\n {\n $expected = $this->reportUuid . '_not_generated_' . $this->recipientEmail;\n $this->assertEquals($expected, $this->job->uniqueId());\n }\n\n public function testHandleSendsEmailSuccessfully(): void\n {\n Config::set('mail.from.address', 'noreply@jiminny.com');\n Config::set('mail.from.name', 'Jiminny');\n Config::set('jiminny.deploy_region', 'us');\n Config::set('logos.cdn.header', 'https://cdn.test.com/header.png');\n Config::set('logos.cdn.footer', 'https://cdn.test.com/footer.png');\n\n $this->logger->expects($this->once())\n ->method('info')\n ->with('[Send Report Not Generated Mail] Email sent', [\n 'uuid' => $this->reportUuid,\n 'email' => $this->recipientEmail,\n 'recipientName' => $this->recipientName,\n ]);\n\n $this->job->handle($this->logger);\n\n Mail::assertQueued(ReportNotGenerated::class, function ($mail) {\n return $mail->hasTo($this->recipientEmail);\n });\n }\n\n public function testHandleLogsErrorAndRetriesOnException(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error')\n ->with('[Send Report Not Generated Mail] Error sending email', $this->callback(function ($context) {\n return $context['uuid'] === $this->reportUuid\n && $context['email'] === $this->recipientEmail\n && $context['error'] === 'SMTP error'\n && isset($context['trace']);\n }));\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['release'])\n ->getMock();\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(60);\n\n $jobMock->handle($this->logger);\n }\n\n public function testHandleFailsJobAfterMaxAttempts(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'fail'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(3);\n\n $jobMock->expects($this->once())\n ->method('fail')\n ->with($this->isInstanceOf(Exception::class));\n\n $jobMock->handle($this->logger);\n }\n\n public function testJobPropertiesAreSetCorrectly(): void\n {\n $this->assertEquals(3, $this->job->tries);\n $this->assertEquals([60, 300, 600], $this->job->backoff);\n $this->assertEquals(60, $this->job->timeout);\n }\n\n public function testHandleUsesCorrectBackoffForSecondAttempt(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'release'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(2);\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(300);\n\n $jobMock->handle($this->logger);\n }\n\n}","depth":4,"bounds":{"left":0.3799867,"top":0.013567438,"width":0.2393617,"height":0.9656824},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Jobs\\AutomatedReports;\n\nuse Exception;\nuse Illuminate\\Support\\Facades\\Config;\nuse Illuminate\\Support\\Facades\\Mail;\nuse Jiminny\\Jobs\\AutomatedReports\\SendReportNotGeneratedMailJob;\nuse Jiminny\\Mail\\Reports\\ReportNotGenerated;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Psr\\Log\\LoggerInterface;\nuse Tests\\TestCase;\n\nclass SendReportNotGeneratedMailJobTest extends TestCase\n{\n private LoggerInterface&MockObject $logger;\n private SendReportNotGeneratedMailJob $job;\n private string $reportUuid = 'test-uuid';\n private string $recipientEmail;\n private string $recipientName;\n private string $reportName;\n private string $periodName;\n private string $reportsPageUrl;\n\n protected function setUp(): void\n {\n parent::setUp();\n\n $this->recipientEmail = 'user@example.com';\n $this->recipientName = 'Test User';\n $this->reportName = 'Exec Summary';\n $this->periodName = 'Q3 2025';\n $this->reportsPageUrl = 'https://app.jiminny.com/reports';\n\n $this->logger = $this->createMock(LoggerInterface::class);\n\n Config::set('app.env', 'testing');\n\n Mail::fake();\n\n $this->job = new SendReportNotGeneratedMailJob(\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl\n );\n }\n\n public function testUniqueIdReturnsExpectedFormat(): void\n {\n $expected = $this->reportUuid . '_not_generated_' . $this->recipientEmail;\n $this->assertEquals($expected, $this->job->uniqueId());\n }\n\n public function testHandleSendsEmailSuccessfully(): void\n {\n Config::set('mail.from.address', 'noreply@jiminny.com');\n Config::set('mail.from.name', 'Jiminny');\n Config::set('jiminny.deploy_region', 'us');\n Config::set('logos.cdn.header', 'https://cdn.test.com/header.png');\n Config::set('logos.cdn.footer', 'https://cdn.test.com/footer.png');\n\n $this->logger->expects($this->once())\n ->method('info')\n ->with('[Send Report Not Generated Mail] Email sent', [\n 'uuid' => $this->reportUuid,\n 'email' => $this->recipientEmail,\n 'recipientName' => $this->recipientName,\n ]);\n\n $this->job->handle($this->logger);\n\n Mail::assertQueued(ReportNotGenerated::class, function ($mail) {\n return $mail->hasTo($this->recipientEmail);\n });\n }\n\n public function testHandleLogsErrorAndRetriesOnException(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error')\n ->with('[Send Report Not Generated Mail] Error sending email', $this->callback(function ($context) {\n return $context['uuid'] === $this->reportUuid\n && $context['email'] === $this->recipientEmail\n && $context['error'] === 'SMTP error'\n && isset($context['trace']);\n }));\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['release'])\n ->getMock();\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(60);\n\n $jobMock->handle($this->logger);\n }\n\n public function testHandleFailsJobAfterMaxAttempts(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->with($this->recipientEmail)\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'fail'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(3);\n\n $jobMock->expects($this->once())\n ->method('fail')\n ->with($this->isInstanceOf(Exception::class));\n\n $jobMock->handle($this->logger);\n }\n\n public function testJobPropertiesAreSetCorrectly(): void\n {\n $this->assertEquals(3, $this->job->tries);\n $this->assertEquals([60, 300, 600], $this->job->backoff);\n $this->assertEquals(60, $this->job->timeout);\n }\n\n public function testHandleUsesCorrectBackoffForSecondAttempt(): void\n {\n Mail::shouldReceive('mailer')\n ->with('postmark')\n ->andReturnSelf()\n ->shouldReceive('to')\n ->andThrow(new Exception('SMTP error'));\n\n $this->logger->expects($this->once())\n ->method('error');\n\n $jobMock = $this->getMockBuilder(SendReportNotGeneratedMailJob::class)\n ->setConstructorArgs([\n $this->reportUuid,\n $this->recipientEmail,\n $this->recipientName,\n $this->reportName,\n $this->periodName,\n $this->reportsPageUrl,\n ])\n ->onlyMethods(['attempts', 'release'])\n ->getMock();\n\n $jobMock->expects($this->atLeastOnce())\n ->method('attempts')\n ->willReturn(2);\n\n $jobMock->expects($this->once())\n ->method('release')\n ->with(300);\n\n $jobMock->handle($this->logger);\n }\n\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-5346950300665258389
|
8405655363615669016
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
SendReportNotGeneratedMailJobTes…edFormat
Run 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
Debug 'SendReportNotGeneratedMailJobTest.testUniqueIdReturnsExpectedFormat'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
3
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Jobs\AutomatedReports;
use Exception;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Mail;
use Jiminny\Jobs\AutomatedReports\SendReportNotGeneratedMailJob;
use Jiminny\Mail\Reports\ReportNotGenerated;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Tests\TestCase;
class SendReportNotGeneratedMailJobTest extends TestCase
{
private LoggerInterface&MockObject $logger;
private SendReportNotGeneratedMailJob $job;
private string $reportUuid = 'test-uuid';
private string $recipientEmail;
private string $recipientName;
private string $reportName;
private string $periodName;
private string $reportsPageUrl;
protected function setUp(): void
{
parent::setUp();
$this->recipientEmail = '[EMAIL]';
$this->recipientName = 'Test User';
$this->reportName = 'Exec Summary';
$this->periodName = 'Q3 2025';
$this->reportsPageUrl = '[URL_WITH_CREDENTIALS] array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
76285
|
|
76776
|
1925
|
19
|
2026-04-24T08:24:46.691456+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777019086691_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20738-debug-AJ-tracking-UP, menu","depth":5,"help_text":"Git Branch: JY-20738-debug-AJ-tracking-UP","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-4884021265479652975
|
8405593994972867482
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide...
|
76772
|
|
76778
|
1925
|
20
|
2026-04-24T08:24:48.085560+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777019088085_m1.jpg...
|
PhpStorm
|
faVsco.js – custom.log
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
.cursor, folder
.github
.sonarlint, folder
.vscode, folder
.windsurf, folder
app, sources root
Actions, folder
Component, folder
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20738-debug-AJ-tracking-UP, menu","depth":5,"help_text":"Git Branch: JY-20738-debug-AJ-tracking-UP","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"role_description":"text"}]...
|
-5687799803346889642
|
8405592758022155034
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
.cursor, folder
.github
.sonarlint, folder
.vscode, folder
.windsurf, folder
app, sources root
Actions, folder
Component, folder
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder...
|
NULL
|
|
196
|
6
|
0
|
2026-04-09T17:16:02.575402+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-09/1775 /Users/lukas/.screenpipe/data/data/2026-04-09/1775754962575_m1.jpg...
|
iTerm2
|
screenpipe"
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
ggml_metal_init: use concurrency = true
ggml_me ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting conti...
|
[{"role":"AXTextArea","text [{"role":"AXTextArea","text":"ggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events\n2026-04-09T20:14:55.597138Z INFO screenpipe_audio::device::device_manager: starting recording for device: System Audio (output)\n2026-04-09T20:14:55.597260Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:14:55.974621Z INFO screenpipe_audio::device::device_manager: starting recording for device: soundcore AeroClip (input)\n2026-04-09T20:14:55.974675Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for soundcore AeroClip (input) (30s segments)\n2026-04-09T20:15:12.053186Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-206579384513749361, trigger=visual_change)\n2026-04-09T20:15:18.079345Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=3072302384101791222, trigger=visual_change)\n2026-04-09T20:15:25.327517Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6215291834790472374, trigger=click)\n2026-04-09T20:15:28.127631Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction: found 57 eligible frames\n2026-04-09T20:15:31.629118Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction: 56 frames, 25.0MB → 0.9MB (27.5x), 56 JPEGs deleted","depth":4,"value":"ggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\nggml_metal_free: deallocating\n2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks\n2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect\n2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests\n2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:19.728814Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n2026-04-09T20:13:20.730198Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:13:20.730251Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)\n^C2026-04-09T20:13:21.583344Z INFO screenpipe: received ctrl+c, initiating shutdown\n2026-04-09T20:13:21.583454Z INFO screenpipe_audio::device::device_manager: Stopping device: soundcore AeroClip (input)\n2026-04-09T20:13:21.583495Z INFO screenpipe_audio::device::device_manager: Stopping device: System Audio (output)\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ 2026-04-09T20:13:21.664605Z INFO screenpipe_audio::audio_manager::manager: audio manager stopped\n2026-04-09T20:13:21.664696Z INFO screenpipe: stopping UI event capture\n2026-04-09T20:13:21.664730Z INFO screenpipe: received shutdown signal for VisionManager\n2026-04-09T20:13:21.664746Z INFO screenpipe_engine::vision_manager::manager: Shutting down VisionManager\n2026-04-09T20:13:21.664751Z INFO screenpipe_engine::vision_manager::manager: Stopping VisionManager\n2026-04-09T20:13:21.664756Z INFO screenpipe_engine::vision_manager::manager: Stopping vision recording for monitor 1\n2026-04-09T20:13:21.664776Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker shutting down\n2026-04-09T20:13:21.664808Z INFO screenpipe_engine::meeting_detector: meeting v2: shutdown received, exiting detection loop\n2026-04-09T20:13:21.724168Z INFO screenpipe_engine::ui_recorder: UI recording session ended: 3b59b1d1-d80e-4689-8c72-6476af4571df\n2026-04-09T20:13:21.724263Z INFO screenpipe: shutdown complete\nggml_metal_free: deallocating\n\nlukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ npx screenpipe@latest record\ndetected hardware tier: Mid\nchecking permissions...\n screen recording: ok\n microphone: ok\n accessibility: ok\n2026-04-09T20:14:27.098522Z INFO screenpipe_screen::monitor::macos_version: Detected macOS version: 14.6\n2026-04-09T20:14:27.912703Z INFO screenpipe_engine::sleep_monitor: Starting macOS sleep/wake monitor\n2026-04-09T20:14:27.914483Z INFO screenpipe: meeting detector enabled — independent of transcription mode\n2026-04-09T20:14:27.914498Z INFO screenpipe_engine::sleep_monitor: Screen lock/unlock observers registered (CFNotificationCenter)\n2026-04-09T20:14:27.914906Z INFO screenpipe_engine::sleep_monitor: Display reconfiguration watcher registered (CGDisplayRegisterReconfigurationCallback)\n2026-04-09T20:14:28.126293Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction worker started (min_age=600s, poll=300s)\n2026-04-09T20:14:28.126285Z INFO screenpipe_engine::power::manager: power manager started (poll interval: 10s)\n2026-04-09T20:14:28.126333Z INFO screenpipe_engine::vision_manager::manager: Starting VisionManager\n2026-04-09T20:14:28.126772Z INFO screenpipe_core::pipes: loaded pipe: day-recap\n2026-04-09T20:14:28.127021Z INFO screenpipe_core::pipes: loaded pipe: standup-update\n2026-04-09T20:14:28.127561Z INFO screenpipe_core::pipes: loaded pipe: ai-habits\n2026-04-09T20:14:28.127719Z INFO screenpipe_core::pipes: loaded pipe: time-breakdown\n2026-04-09T20:14:28.127878Z INFO screenpipe_core::pipes: loaded pipe: video-export\n2026-04-09T20:14:28.128872Z INFO screenpipe_core::pipes: loaded pipe: meeting-summary\n2026-04-09T20:14:28.128896Z INFO screenpipe_core::pipes: loaded 6 pipes from \"/Users/lukas/.screenpipe/pipes\"\n\n\n\n _ \n __________________ ___ ____ ____ (_____ ___ \n / ___/ ___/ ___/ _ \\/ _ \\/ __ \\ / __ \\/ / __ \\/ _ \\\n (__ / /__/ / / __/ __/ / / / / /_/ / / /_/ / __/\n/____/\\___/_/ \\___/\\___/_/ /_/ / .___/_/ .___/\\___/ \n /_/ /_/ \n\n\n\npower AI by everything you've seen, said or heard\nopen source | runs locally | developer friendly\n\n\n┌────────────────────────┬────────────────────────────────────┐\n│ setting │ value │\n├────────────────────────┼────────────────────────────────────┤\n│ audio chunk duration │ 30 seconds │\n│ port │ 3030 │\n│ audio disabled │ false │\n│ vision disabled │ false │\n│ pause on DRM content │ false │\n│ audio engine │ Parakeet │\n│ vad engine │ Silero │\n│ data directory │ /Users/lukas/.screenpipe │\n│ debug mode │ false │\n│ telemetry │ true │\n│ use pii removal │ true │\n│ use all monitors │ true │\n│ ignored windows │ [] │\n│ included windows │ [] │\n│ cloud sync │ disabled │\n│ auto-destruct pid │ 0 │\n│ deepgram key │ not set │\n├────────────────────────┼────────────────────────────────────┤\n2026-04-09T20:14:28.130298Z INFO screenpipe_core::pipes: pipe scheduler started (generation 2)\n│ languages │ │\n│ │ all languages │\n├────────────────────────┼────────────────────────────────────┤\n│ monitors │ │\n│ │ no monitors available │\n├────────────────────────┼────────────────────────────────────┤\n│ audio devices │ │\n│ │ soundcore AeroClip (input) │\n│ │ System Audio (output) │\n└────────────────────────┴────────────────────────────────────┘\nyou are using local processing. all your data stays on your computer.\n\nwarning: telemetry is enabled. only error-level data will be sent.\nto disable, use the --disable-telemetry flag.\n\ncheck latest changes here: https://github.com/screenpipe/screenpipe/releases\n2026-04-09T20:14:28.130784Z INFO screenpipe: starting UI event capture\n2026-04-09T20:14:28.133972Z WARN screenpipe: pi agent install failed: bun not found — install from https://bun.sh\n2026-04-09T20:14:28.135954Z INFO screenpipe_engine::power::manager: initial power profile: Performance (on_ac=true, battery=Some(100))\n2026-04-09T20:14:28.145308Z INFO screenpipe_engine::ui_recorder: Starting UI event capture\n2026-04-09T20:14:28.159511Z INFO screenpipe_engine::ui_recorder: UI recording session started: 80e2e759-7433-4879-97f3-223fcd952b7a\n2026-04-09T20:14:28.159557Z INFO screenpipe_engine::calendar_speaker_id: speaker identification: started (user_name=<not set>)\n2026-04-09T20:14:28.159589Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warming from DB (2026-04-08 17:14:28.159589 UTC to 2026-04-09 17:14:28.159589 UTC)\n2026-04-09T20:14:28.160424Z INFO screenpipe_engine::meeting_detector: meeting v2: detection loop started (base_interval=5s, profiles=12)\n2026-04-09T20:14:28.171304Z INFO screenpipe_engine::server: Server listening on 0.0.0.0:3030\n2026-04-09T20:14:28.185141Z INFO screenpipe_connect::mdns: mdns: advertising screenpipe on port 3030\n2026-04-09T20:14:28.214853Z INFO screenpipe_engine::hot_frame_cache: hot_frame_cache: warmed with 175 frame entries, coverage from 2026-04-08 17:14:28.159589 UTC\n2026-04-09T20:14:28.527279Z INFO screenpipe_engine::vision_manager::manager: Starting vision recording for monitor 1 (1440x900)\n2026-04-09T20:14:28.527311Z INFO screenpipe_engine::vision_manager::manager: Starting event-driven capture for monitor 1 (device: monitor_1)\n2026-04-09T20:14:28.527322Z INFO screenpipe_engine::vision_manager::manager: Skipping monitor 2 (Display 2_3008x1253_-813,-1253) — not in allowed list\n2026-04-09T20:14:28.527330Z INFO screenpipe_engine::vision_manager::monitor_watcher: Starting monitor watcher (polling every 5 seconds)\n2026-04-09T20:14:28.527387Z INFO screenpipe_engine::event_driven_capture: event-driven capture started for monitor 1 (device: monitor_1)\n2026-04-09T20:14:29.374206Z INFO sck_rs::stream_manager: persistent SCK stream started for display 1 (1440x900, 2fps)\n2026-04-09T20:14:33.163940Z INFO screenpipe_engine::retention: retention: initialized with 14d retention\n2026-04-09T20:14:33.164688Z INFO screenpipe: local retention auto-enabled (14 days)\n2026-04-09T20:14:38.134070Z INFO screenpipe_audio::transcription::engine: whisper model available: \"/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin\"\n2026-04-09T20:14:38.134185Z INFO screenpipe_audio::transcription::whisper::model: whisper context: gpu acceleration enabled (Metal on macOS, Vulkan on Windows)\n2026-04-09T20:14:38.134200Z INFO screenpipe_audio::transcription::engine: loading whisper model with GPU acceleration...\nwhisper_init_from_file_with_params_no_state: loading model from '/Users/lukas/.cache/huggingface/hub/models--ggerganov--whisper.cpp/snapshots/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.bin'\nwhisper_init_with_params_no_state: use gpu = 1\nwhisper_init_with_params_no_state: flash attn = 0\nwhisper_init_with_params_no_state: gpu_device = 0\nwhisper_init_with_params_no_state: dtw = 0\nggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices\nggml_metal_library_init: using embedded metal library\n2026-04-09T20:14:38.385637Z INFO screenpipe_engine::event_driven_capture: startup capture for monitor 1: frame_id=176, dur=31ms\nggml_metal_library_init: loaded in 15.792 sec\nggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)\nggml_metal_device_init: GPU name: Apple M1\nggml_metal_device_init: GPU family: MTLGPUFamilyApple7 (1007)\nggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)\nggml_metal_device_init: GPU family: MTLGPUFamilyMetal3 (5001)\nggml_metal_device_init: simdgroup reduction = true\nggml_metal_device_init: simdgroup matrix mul. = true\nggml_metal_device_init: has unified memory = true\nggml_metal_device_init: has bfloat = true\nggml_metal_device_init: has tensor = false\nggml_metal_device_init: use residency sets = true\nggml_metal_device_init: use shared buffers = true\nggml_metal_device_init: recommendedMaxWorkingSetSize = 11453.25 MB\nwhisper_init_with_params_no_state: devices = 3\nwhisper_init_with_params_no_state: backends = 3\nwhisper_model_load: loading model\nwhisper_model_load: n_vocab = 51865\nwhisper_model_load: n_audio_ctx = 1500\nwhisper_model_load: n_audio_state = 384\nwhisper_model_load: n_audio_head = 6\nwhisper_model_load: n_audio_layer = 4\nwhisper_model_load: n_text_ctx = 448\nwhisper_model_load: n_text_state = 384\nwhisper_model_load: n_text_head = 6\nwhisper_model_load: n_text_layer = 4\nwhisper_model_load: n_mels = 80\nwhisper_model_load: ftype = 1\nwhisper_model_load: qntvr = 0\nwhisper_model_load: type = 1 (tiny)\nwhisper_model_load: adding 1608 extra tokens\nwhisper_model_load: n_langs = 99\nwhisper_model_load: Metal total size = 77.11 MB\nwhisper_model_load: model size = 77.11 MB\n2026-04-09T20:14:54.059372Z INFO screenpipe_audio::transcription::engine: whisper model loaded successfully\nwhisper_backend_init_gpu: device 0: Metal (type: 1)\nwhisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)\nwhisper_backend_init_gpu: using Metal backend\nggml_metal_init: allocating\nggml_metal_init: found device: Apple M1\nggml_metal_init: picking default device: Apple M1\nggml_metal_init: use fusion = true\nggml_metal_init: use concurrency = true\nggml_metal_init: use graph optimize = true\nwhisper_backend_init: using BLAS backend\nwhisper_init_state: kv self size = 3.15 MB\nwhisper_init_state: kv cross size = 9.44 MB\nwhisper_init_state: kv pad size = 2.36 MB\nwhisper_init_state: compute buffer (conv) = 14.17 MB\nwhisper_init_state: compute buffer (encode) = 65.96 MB\nwhisper_init_state: compute buffer (cross) = 8.50 MB\nwhisper_init_state: compute buffer (decode) = 96.83 MB\n2026-04-09T20:14:54.062532Z INFO screenpipe_audio::audio_manager::manager: transcription session created (will be reused across segments)\n2026-04-09T20:14:54.062898Z INFO screenpipe_audio::audio_manager::manager: audio manager started\n2026-04-09T20:14:54.062937Z INFO screenpipe_audio::audio_manager::manager: calendar-assisted speaker diarization: listening for meeting events\n2026-04-09T20:14:55.597138Z INFO screenpipe_audio::device::device_manager: starting recording for device: System Audio (output)\n2026-04-09T20:14:55.597260Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)\n2026-04-09T20:14:55.974621Z INFO screenpipe_audio::device::device_manager: starting recording for device: soundcore AeroClip (input)\n2026-04-09T20:14:55.974675Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for soundcore AeroClip (input) (30s segments)\n2026-04-09T20:15:12.053186Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-206579384513749361, trigger=visual_change)\n2026-04-09T20:15:18.079345Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=3072302384101791222, trigger=visual_change)\n2026-04-09T20:15:25.327517Z INFO screenpipe_engine::event_driven_capture: content dedup: skipping capture for monitor 1 (hash=-6215291834790472374, trigger=click)\n2026-04-09T20:15:28.127631Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction: found 57 eligible frames\n2026-04-09T20:15:31.629118Z INFO screenpipe_engine::snapshot_compaction: snapshot compaction: 56 frames, 25.0MB → 0.9MB (27.5x), 56 JPEGs deleted","is_focused":true},{"role":"AXRadioButton","text":"DOCKER","depth":2,"bounds":{"left":0.0,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.004166667,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"DEV (-zsh)","depth":2,"bounds":{"left":0.16458334,"top":0.05888889,"width":0.16458334,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.16875,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"APP (-zsh)","depth":2,"bounds":{"left":0.32916668,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.33333334,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"screenpipe\"","depth":2,"bounds":{"left":0.49340278,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.49756944,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"✳ Check today's app usage (node)","depth":2,"bounds":{"left":0.6576389,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.66180557,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXRadioButton","text":"-zsh","depth":2,"bounds":{"left":0.821875,"top":0.05888889,"width":0.16423611,"height":0.026666667},"role_description":"radio button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Close Tab","depth":3,"bounds":{"left":0.82604164,"top":0.06333333,"width":0.011111111,"height":0.017777778},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"⌥⌘1","depth":1,"bounds":{"left":0.95763886,"top":0.032222223,"width":0.03888889,"height":0.018888889},"automation_id":"_NS:8","role_description":"text"},{"role":"AXStaticText","text":"screenpipe\"","depth":1,"bounds":{"left":0.4722222,"top":0.033333335,"width":0.058333334,"height":0.017777778},"role_description":"text"}]...
|
-4426038861804327400
|
8404915130603599337
|
click
|
accessibility
|
NULL
|
ggml_metal_init: use concurrency = true
ggml_me ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
whisper_backend_init_gpu: device 0: Metal (type: 1)
whisper_backend_init_gpu: found GPU device 0: Metal (type: 1, cnt: 0)
whisper_backend_init_gpu: using Metal backend
ggml_metal_init: allocating
ggml_metal_init: found device: Apple M1
ggml_metal_init: picking default device: Apple M1
ggml_metal_init: use fusion = true
ggml_metal_init: use concurrency = true
ggml_metal_init: use graph optimize = true
whisper_backend_init: using BLAS backend
whisper_init_state: kv self size = 3.15 MB
whisper_init_state: kv cross size = 9.44 MB
whisper_init_state: kv pad size = 2.36 MB
whisper_init_state: compute buffer (conv) = 14.17 MB
whisper_init_state: compute buffer (encode) = 65.96 MB
whisper_init_state: compute buffer (cross) = 8.50 MB
whisper_init_state: compute buffer (decode) = 96.83 MB
ggml_metal_free: deallocating
2026-04-09T20:11:31.943416Z INFO screenpipe_audio::audio_manager::manager: reconciliation: transcribed 50 orphaned chunks
2026-04-09T20:12:23.632839Z WARN screenpipe_audio::core::run_record_and_transcribe: no audio received from System Audio (output) for 30s - stream dead, triggering reconnect
2026-04-09T20:12:24.634524Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:24.634717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:25.636167Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:25.636241Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:26.637475Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:26.637632Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:27.639481Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:27.639674Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:28.641392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:28.641468Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:29.643401Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:29.643465Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:30.645555Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:30.645736Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:31.646650Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:31.646804Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:32.648705Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:32.648787Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:33.649937Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:33.650126Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:34.652420Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:34.652503Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:35.654363Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:35.654429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:36.655671Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:36.655835Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:37.657385Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:37.657510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:38.659335Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:38.659397Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:39.660525Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:39.660601Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:40.662552Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:40.662717Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:41.664366Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:41.664429Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:42.666360Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:42.666464Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:43.668128Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:43.668333Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:44.670229Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:44.670277Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:45.671405Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:45.671453Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:46.672748Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:46.672859Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:47.673601Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:47.673766Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:48.676092Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:48.676148Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:49.677349Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:49.677498Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:50.679547Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:50.679705Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:51.681540Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:51.681638Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:52.682700Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:52.682893Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:53.683432Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:53.683587Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:54.685305Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:54.685380Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:55.687119Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:55.687360Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:56.688435Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:56.688513Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:57.690272Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:57.690340Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:58.692254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:58.692310Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:12:59.693320Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:12:59.693493Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:00.694348Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:00.694433Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:01.696392Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:01.696444Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:02.698281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:02.698458Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:03.700030Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:03.700146Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:04.702226Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:04.702294Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:05.704281Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:05.704359Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:06.705808Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:06.705920Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:07.707231Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:07.707298Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:08.182408Z INFO screenpipe_engine::server: api_usage_5min: 2 requests
2026-04-09T20:13:08.708393Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:08.708510Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:09.710915Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:09.710974Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:10.712639Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:10.712711Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:11.714135Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:11.714182Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:12.716251Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:12.716313Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:13.717538Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:13.717682Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:14.719220Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:14.719306Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:15.721206Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:15.721607Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:16.723013Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:16.723167Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:17.725383Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:17.725436Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:18.727254Z INFO screenpipe_audio::core::run_record_and_transcribe: starting continuous recording for System Audio (output) (30s segments)
2026-04-09T20:13:18.727423Z INFO screenpipe_audio::core::run_record_and_transcribe: stopped recording for System Audio (output) (disconnected)
2026-04-09T20:13:19.728646Z INFO screenpipe_audio::core::run_record_and_transcribe: starting conti...
|
NULL
|
|
76266
|
1909
|
3
|
2026-04-24T07:39:01.396415+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016341396_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-8258260772794833742
|
8404748264376863642
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide...
|
76265
|
|
76267
|
1910
|
3
|
2026-04-24T07:39:01.342781+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016341342_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.89944136,"width":0.100398935,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"bounds":{"left":0.8753325,"top":0.915403,"width":0.11037234,"height":0.027134877},"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.915403,"width":0.09507979,"height":0.027134877},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.59541225,"top":0.32322428,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6050532,"top":0.3216281,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.61236703,"top":0.3216281,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"bounds":{"left":0.3799867,"top":0.0,"width":0.34375,"height":1.0},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
8411209987128727906
|
8404748264376863642
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes...
|
76264
|
|
76211
|
1907
|
26
|
2026-04-24T07:36:39.394013+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016199394_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Patch successfully applied
text/html
text/html
tex Patch successfully applied
text/html
text/html
text/html
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification...
|
[{"role":"AXTextField","text [{"role":"AXTextField","text":"Patch successfully applied","depth":3,"value":"Patch successfully applied","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
6595782366022348956
|
8404748264372669338
|
click
|
accessibility
|
NULL
|
Patch successfully applied
text/html
text/html
tex Patch successfully applied
text/html
text/html
text/html
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification...
|
NULL
|
|
52541
|
1137
|
25
|
2026-04-20T07:21:29.324879+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776669689324_m2.jpg...
|
PhpStorm
|
faVsco.js – console [EU]
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Laravel Idea: Generate Helper Code?
Helper Code wi Laravel Idea: Generate Helper Code?
Helper Code will help IDE to understand your Laravel app code.
text/html
text/html
text/html
Generate
Don't Show Anymore
More
The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.
text/html
text/html
text/html
Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Laravel Idea: Generate Helper Code?","depth":2,"bounds":{"left":0.8753325,"top":0.8172386,"width":0.100398935,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"Helper Code will help IDE to understand your Laravel app code.","depth":3,"bounds":{"left":0.8753325,"top":0.83320034,"width":0.11037234,"height":0.027134877},"value":"Helper Code will help IDE to understand your Laravel app code.","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.83320034,"width":0.09275266,"height":0.027134877},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Generate","depth":2,"bounds":{"left":0.8753325,"top":0.8659218,"width":0.018949468,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Don't Show Anymore","depth":2,"bounds":{"left":0.89960104,"top":0.8659218,"width":0.04288564,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"More","depth":2,"bounds":{"left":0.27027926,"top":1.0,"width":0.016289894,"height":0.0},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.","depth":3,"bounds":{"left":0.8753325,"top":0.9074222,"width":0.11037234,"height":0.054269753},"value":"The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.9074222,"width":0.095744684,"height":0.054269753},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11894 on JY-18909-automated-reports-ask-jiminny, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #11894 exists for current branch JY-18909-automated-reports-ask-jiminny","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"bounds":{"left":0.36868352,"top":0.2490024,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"bounds":{"left":0.37865692,"top":0.2490024,"width":0.008976064,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.38929522,"top":0.24740623,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.39660904,"top":0.24740623,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
4891451058990452583
|
8404467957765491610
|
visual_change
|
accessibility
|
NULL
|
Laravel Idea: Generate Helper Code?
Helper Code wi Laravel Idea: Generate Helper Code?
Helper Code will help IDE to understand your Laravel app code.
text/html
text/html
text/html
Generate
Don't Show Anymore
More
The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.
text/html
text/html
text/html
Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}...
|
NULL
|
|
52537
|
1136
|
19
|
2026-04-20T07:21:26.073799+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776669686073_m1.jpg...
|
PhpStorm
|
faVsco.js – console [EU]
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Laravel Idea: Generate Helper Code?
Helper Code wi Laravel Idea: Generate Helper Code?
Helper Code will help IDE to understand your Laravel app code.
text/html
text/html
text/html
Generate
Don't Show Anymore
More
The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.
text/html
text/html
text/html
Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"Laravel Idea: Generate Helper Code?","depth":2,"role_description":"text"},{"role":"AXTextField","text":"Helper Code will help IDE to understand your Laravel app code.","depth":3,"value":"Helper Code will help IDE to understand your Laravel app code.","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Generate","depth":2,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Don't Show Anymore","depth":2,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"More","depth":2,"bounds":{"left":0.0,"top":0.0,"width":0.034027778,"height":0.018888889},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.","depth":3,"value":"The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11894 on JY-18909-automated-reports-ask-jiminny, menu","depth":5,"help_text":"Pull request #11894 exists for current branch JY-18909-automated-reports-ask-jiminny","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"2","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7420632223208125730
|
8404467957698366362
|
click
|
accessibility
|
NULL
|
Laravel Idea: Generate Helper Code?
Helper Code wi Laravel Idea: Generate Helper Code?
Helper Code will help IDE to understand your Laravel app code.
text/html
text/html
text/html
Generate
Don't Show Anymore
More
The Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages.
text/html
text/html
text/html
Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto...
|
NULL
|
|
52115
|
1127
|
27
|
2026-04-20T06:40:01.595018+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776667201595_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11894 on JY-18909-automated-reports-ask-jiminny, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #11894 exists for current branch JY-18909-automated-reports-ask-jiminny","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"role_description":"text"},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7585605862760607160
|
8404467957564156826
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}...
|
52114
|
|
52116
|
1126
|
27
|
2026-04-20T06:40:02.243170+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776667202243_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11894 on JY-18909-automated-reports-ask-jiminny, menu","depth":5,"help_text":"Pull request #11894 exists for current branch JY-18909-automated-reports-ask-jiminny","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"role_description":"text"},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.015277778,"height":0.025555555},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.014583333,"height":0.025555555},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7585605862760607160
|
8404467957564156826
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}...
|
52111
|
|
52166
|
1129
|
5
|
2026-04-20T06:42:50.311905+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776667370311_m2.jpg...
|
PhpStorm
|
faVsco.js – console [EU]
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11894 on JY-18909-automated-reports-ask-jiminny, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #11894 exists for current branch JY-18909-automated-reports-ask-jiminny","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"4","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"role_description":"text"},{"role":"AXStaticText","text":"8","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
-7585605862760607160
|
8404467957564156826
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
4
8
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}...
|
52164
|
|
52662
|
1139
|
41
|
2026-04-20T07:27:48.997692+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776670068997_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
6
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.025930852,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11894 on JY-18909-automated-reports-ask-jiminny, menu","depth":5,"bounds":{"left":0.064494684,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #11894 exists for current branch JY-18909-automated-reports-ask-jiminny","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.007978723,"height":0.0},"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008976064,"height":0.0},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.00731383,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.006981383,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"bounds":{"left":0.40492022,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"bounds":{"left":0.41356382,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"bounds":{"left":0.4245346,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"bounds":{"left":0.4331782,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"bounds":{"left":0.4418218,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"bounds":{"left":0.45279256,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"bounds":{"left":0.4637633,"top":0.09896249,"width":0.024268618,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"bounds":{"left":0.49035904,"top":0.09896249,"width":0.008643617,"height":0.01915403},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"bounds":{"left":0.5013298,"top":0.09896249,"width":0.029587766,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"bounds":{"left":0.69913566,"top":0.09896249,"width":0.02825798,"height":0.01915403},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2066644074886763499
|
8404467957564148634
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
6
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:...
|
52656
|
|
52687
|
1140
|
5
|
2026-04-20T07:29:52.930386+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-20/1776 /Users/lukas/.screenpipe/data/data/2026-04-20/1776670192930_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
6
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#11894 on JY-18909-automated-reports-ask-jiminny, menu","depth":5,"help_text":"Pull request #11894 exists for current branch JY-18909-automated-reports-ask-jiminny","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"6","depth":4,"role_description":"text"},{"role":"AXStaticText","text":"11","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn(null);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Execute","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Explain Plan","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Browse Query History","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"View Parameters","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Open Query Execution Settings…","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"In-Editor Results","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Tx: Auto","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Cancel Running Statements","depth":4,"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Playground","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"jiminny","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
2066644074886763499
|
8404467957564148634
|
visual_change
|
accessibility
|
NULL
|
Project: faVsco.js, menu
#11894 on JY-18909-automa Project: faVsco.js, menu
#11894 on JY-18909-automated-reports-ask-jiminny, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
6
11
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksCreatorForAskJiminnyReportWithNullFrequency(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn(null);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => null]
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->once())->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Execute
Explain Plan
Browse Query History
View Parameters
Open Query Execution Settings…
In-Editor Results
Tx: Auto
Cancel Running Statements
Playground
jiminny
Sync Changes
Hide This Notification
Code changed:...
|
52684
|
|
76784
|
1925
|
23
|
2026-04-24T08:24:54.235372+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777019094235_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
.cursor, folder
.github
.sonarlint, folder
.vscode, folder
.windsurf, folder
app, sources root
Actions, folder
Component, folder
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder
Calendars, folder
Crm, folder
Hubspot, folder
IntegrationApp, folder
Traits, folder
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20738-debug-AJ-tracking-UP, menu","depth":5,"help_text":"Git Branch: JY-20738-debug-AJ-tracking-UP","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"AskJiminnyReportActivityServiceTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'AskJiminnyReportActivityServiceTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'AskJiminnyReportActivityServiceTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Utility, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uuid, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Waveform, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Webhooks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Workflow, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Configuration, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Console, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Commands, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activities, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Analytics, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Calendars, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Crm, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Hubspot, folder","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"IntegrationApp, folder","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"Traits, folder","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AddLayoutEntities.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"AutologDelayedCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornCommandAbstract.php, abstract class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornPingCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSearchCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"BullhornSessionCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CheckActivityLoggableCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"CleanDuplicateFieldDataCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"FullSyncOpportunityCommand.php, class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"LogActivitiesCommand.php, final class","depth":11,"role_description":"text"},{"role":"AXStaticText","text":"ManageSyncStrategyCommand.php, class","depth":11,"role_description":"text"}]...
|
6646992787775520800
|
8404467957559831434
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
AskJiminnyReportActivityServiceTest
Run 'AskJiminnyReportActivityServiceTest'
Debug 'AskJiminnyReportActivityServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
.cursor, folder
.github
.sonarlint, folder
.vscode, folder
.windsurf, folder
app, sources root
Actions, folder
Component, folder
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder
Utility, folder
Uuid, folder
Waveform, folder
Webhooks, folder
Workflow, folder
Configuration, folder
Console, folder
Commands, folder
Activities, folder
Analytics, folder
Calendars, folder
Crm, folder
Hubspot, folder
IntegrationApp, folder
Traits, folder
AddLayoutEntities.php, class
AutologDelayedCommand.php, class
BullhornCommandAbstract.php, abstract class
BullhornPingCommand.php, class
BullhornSearchCommand.php, class
BullhornSessionCommand.php, class
CheckActivityLoggableCommand.php, final class
CleanDuplicateFieldDataCommand.php, class
FullSyncOpportunityCommand.php, class
LogActivitiesCommand.php, final class
ManageSyncStrategyCommand.php, class...
|
76782
|
|
76788
|
NULL
|
0
|
2026-04-24T08:25:07.658513+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777019107658_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
.cursor, folder
.github
.sonarlint, folder
.vscode, folder
.windsurf, folder
app, sources root
Actions, folder
Component, folder
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder...
|
[{"role":"AXButton","text" [{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JY-20738-debug-AJ-tracking-UP, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.08510638,"height":0.025538707},"help_text":"Git Branch: JY-20738-debug-AJ-tracking-UP","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.63796544,"top":0.12529927,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6476064,"top":0.123703115,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.6549202,"top":0.123703115,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"bounds":{"left":0.3799867,"top":0.12210695,"width":0.34375,"height":0.87789303},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"app ~/jiminny/app, folder","depth":6,"role_description":"text"},{"role":"AXStaticText","text":".circleci, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".cursor, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".github","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".sonarlint, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".vscode, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":".windsurf, folder","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"app, sources root","depth":7,"role_description":"text"},{"role":"AXStaticText","text":"Actions, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Component, folder","depth":8,"role_description":"text"},{"role":"AXStaticText","text":"Acl, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActionItems, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Activity, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivityAnalytics, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ActivitySearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiActivityType, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiAutomation, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AiCallScoring, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AskAnything, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Dtos, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"Events, folder","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskAnythingPromptService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"HistoryService.php, class","depth":10,"role_description":"text"},{"role":"AXStaticText","text":"AskJiminnyAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"AWS, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"BillingManagement, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Cache, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CoachingFeedback, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Country, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"CustomerApi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Database, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Datadog, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DateTime, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"DealRisks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ElasticSearch, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Eloquent, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encoding, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Encryption, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ES, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Faker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FeatureFlags, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FFMpeg, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"FileSystem, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gecko, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Gong, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"GuzzleHttp, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"KeyPoints, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Kiosk, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LanguageDetection, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"LiveFeed, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Locks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Math, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MediaPipeline, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MeetingBot, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"MobileSettings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Model, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Notification, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Nudge, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParagraphBreaker, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ParticipantSpeech, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PartitionedCookie, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"PlaybackPage, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Playlist, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Prophet, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProphetAi, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"ProsperWorks, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Queue, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Router, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Saml2, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"SCIM, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Seeder, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sentry, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Serializer, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Settings, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Sidekick, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Slack, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TeamInsights, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TimeMemoryMapper, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Transcription, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"TranscriptionSummary, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Twilio, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"Uploader, folder","depth":9,"role_description":"text"},{"role":"AXStaticText","text":"UrlGenerator, folder","depth":9,"role_description":"text"}]...
|
6502072561878735239
|
8404466858121603978
|
click
|
accessibility
|
NULL
|
Project: faVsco.js, menu
JY-20738-debug-AJ-trackin Project: faVsco.js, menu
JY-20738-debug-AJ-tracking-UP, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide
app ~/jiminny/app, folder
.circleci, folder
.cursor, folder
.github
.sonarlint, folder
.vscode, folder
.windsurf, folder
app, sources root
Actions, folder
Component, folder
Acl, folder
ActionItems, folder
Activity, folder
ActivityAnalytics, folder
ActivitySearch, folder
AiActivityType, folder
AiAutomation, folder
AiCallScoring, folder
AskAnything, folder
Dtos, folder
Events, folder
AskAnythingPromptService.php, class
HistoryService.php, class
AskJiminnyAi, folder
AWS, folder
BillingManagement, folder
Cache, folder
CoachingFeedback, folder
Country, folder
CustomerApi, folder
Database, folder
Datadog, folder
DateTime, folder
DealInsights, folder
DealRisks, folder
ElasticSearch, folder
Eloquent, folder
Encoding, folder
Encryption, folder
ES, folder
Faker, folder
FeatureFlags, folder
FFMpeg, folder
FileSystem, folder
Gecko, folder
Gong, folder
GuzzleHttp, folder
KeyPoints, folder
Kiosk, folder
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder
ParagraphBreaker, folder
ParticipantSpeech, folder
PartitionedCookie, folder
PlaybackPage, folder
Playlist, folder
Prophet, folder
ProphetAi, folder
ProsperWorks, folder
Queue, folder
Router, folder
Saml2, folder
SCIM, folder
Seeder, folder
Sentry, folder
Serializer, folder
Settings, folder
Sidekick, folder
Slack, folder
TeamInsights, folder
TimeMemoryMapper, folder
Transcription, folder
TranscriptionSummary, folder
Twilio, folder
Uploader, folder
UrlGenerator, folder...
|
76787
|
|
76264
|
1910
|
2
|
2026-04-24T07:38:51.075772+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016331075_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.89944136,"width":0.100398935,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"bounds":{"left":0.8753325,"top":0.915403,"width":0.11037234,"height":0.027134877},"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.915403,"width":0.09507979,"height":0.027134877},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.59541225,"top":0.32322428,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6050532,"top":0.3216281,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.61236703,"top":0.3216281,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"bounds":{"left":0.3799867,"top":0.0,"width":0.34375,"height":1.0},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7506456211656557156
|
8404466858119637786
|
app_switch
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
76265
|
1909
|
2
|
2026-04-24T07:38:52.745034+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016332745_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7506456211656557156
|
8404466858119637786
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
76268
|
1910
|
4
|
2026-04-24T07:39:03.170910+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016343170_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.89944136,"width":0.100398935,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"bounds":{"left":0.8753325,"top":0.915403,"width":0.11037234,"height":0.027134877},"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.915403,"width":0.09507979,"height":0.027134877},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.59541225,"top":0.32322428,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6050532,"top":0.3216281,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.61236703,"top":0.3216281,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"bounds":{"left":0.3799867,"top":0.0,"width":0.34375,"height":1.0},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7506456211656557156
|
8404466858119637786
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
76269
|
1909
|
4
|
2026-04-24T07:39:03.246123+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016343246_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7506456211656557156
|
8404466858119637786
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
76299
|
1909
|
20
|
2026-04-24T07:40:22.693414+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016422693_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.016666668,"height":0.02111111},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7506456211656557156
|
8404466858119637786
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|
|
76300
|
1910
|
19
|
2026-04-24T07:40:23.371636+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016423371_m2.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_2
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"bounds":{"left":0.8753325,"top":0.89944136,"width":0.100398935,"height":0.013567438},"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"bounds":{"left":0.8753325,"top":0.915403,"width":0.11037234,"height":0.027134877},"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"bounds":{"left":0.8753325,"top":0.915403,"width":0.09507979,"height":0.027134877},"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"bounds":{"left":0.8753325,"top":0.9481245,"width":0.048204787,"height":0.013567438},"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"bounds":{"left":0.25731382,"top":0.019952115,"width":0.03856383,"height":0.025538707},"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"bounds":{"left":0.29587767,"top":0.019952115,"width":0.12134308,"height":0.025538707},"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"bounds":{"left":0.796875,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"bounds":{"left":0.8121675,"top":0.019952115,"width":0.103390954,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9155585,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"bounds":{"left":0.9268617,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"bounds":{"left":0.9381649,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"bounds":{"left":0.96609044,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"bounds":{"left":0.9773936,"top":0.019952115,"width":0.011303191,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"bounds":{"left":0.9886968,"top":0.019952115,"width":0.011303186,"height":0.025538707},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"bounds":{"left":0.59541225,"top":0.32322428,"width":0.007978723,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.6050532,"top":0.3216281,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.61236703,"top":0.3216281,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"bounds":{"left":0.3799867,"top":0.3200319,"width":0.34375,"height":0.6799681},"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":true,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.042220745,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"bounds":{"left":0.96210104,"top":0.10055866,"width":0.010305851,"height":0.015163607},"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"bounds":{"left":0.9740692,"top":0.09896249,"width":0.00731383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"bounds":{"left":0.98138297,"top":0.09896249,"width":0.006981383,"height":0.018355945},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"bounds":{"left":0.24335106,"top":0.047885075,"width":0.024268618,"height":0.024740623},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.27027926,"top":1.0,"width":0.008643617,"height":0.0},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7506456211656557156
|
8404466858119637786
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
76298
|
|
76308
|
1909
|
24
|
2026-04-24T07:40:39.882361+00:00
|
/Users/lukas/.screenpipe/data/data/2026-04-24/1777 /Users/lukas/.screenpipe/data/data/2026-04-24/1777016439882_m1.jpg...
|
PhpStorm
|
faVsco.js – TrackAutomatedReportGeneratedEventTest faVsco.js – TrackAutomatedReportGeneratedEventTest.php...
|
True
|
NULL
|
monitor_1
|
NULL
|
NULL
|
NULL
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
[{"role":"AXStaticText","text& [{"role":"AXStaticText","text":"2 files committed","depth":2,"role_description":"text"},{"role":"AXTextField","text":"JY-20157 move user pilot tracking changes to separated PR","depth":3,"value":"JY-20157 move user pilot tracking changes to separated PR","help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextField","text":"text/html","depth":4,"help_text":"text/html","role_description":"text field","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXLink","text":"Edit Commit Message…","depth":2,"role_description":"link","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Project: faVsco.js, menu","depth":5,"help_text":"~/jiminny/app","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"#12011 on JY-20157-AJ-report-not-send-notification, menu","depth":5,"help_text":"Pull request #12011 exists for current branch JY-20157-AJ-report-not-send-notification","role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Start Listening for PHP Debug Connections","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"TrackAutomatedReportGeneratedEventTest","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Run 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Debug 'TrackAutomatedReportGeneratedEventTest'","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"More Actions","depth":6,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"JetBrains AI","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Search Everywhere","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"IDE and Project Settings","depth":5,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"9","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Code changed:","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.088194445,"height":0.027777778},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Sync Changes","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide This Notification","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":false,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"43","depth":4,"role_description":"text"},{"role":"AXButton","text":"Previous Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Next Highlighted Error","depth":4,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXTextArea","text":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","depth":4,"value":"<?php\n\ndeclare(strict_types=1);\n\nnamespace Jiminny\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Illuminate\\Contracts\\Queue\\ShouldQueue;\nuse Illuminate\\Queue\\InteractsWithQueue;\nuse Jiminny\\Component\\Queue\\Constants;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\Contracts\\UserContract;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse Illuminate\\Support\\Facades\\Log;\n\nclass TrackAutomatedReportGeneratedEvent implements ShouldQueue\n{\n use InteractsWithQueue;\n\n private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';\n private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';\n\n public string $queue = Constants::QUEUE_DELAYABLE;\n\n public function __construct(\n private readonly UserPilotClient $userPilotClient,\n private readonly AutomatedReportsService $automatedReportsService,\n ) {\n }\n\n public function handle(AutomatedReportGenerated $event): void\n {\n if (config('services.userpilot.token') === null) {\n return;\n }\n\n $automatedReport = $event->automatedReport;\n $payload = $this->buildPayload($automatedReport);\n\n $eventName = $this->resolveEventName($automatedReport);\n\n $users = $this->resolveUsers($automatedReport);\n\n if (empty($users)) {\n Log::warning('[UserPilot] No recipients found for automated report', [\n 'report_id' => $automatedReport->getId(),\n 'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),\n ]);\n\n return;\n }\n\n Log::info('[UserPilot] Sending automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'event_name' => $eventName,\n 'recipient_count' => count($users),\n ]);\n\n try {\n foreach ($users as $user) {\n $this->userPilotClient->track($user, $eventName, $payload);\n }\n } catch (GuzzleException $e) {\n Log::error('[UserPilot] Failed to send automated report event', [\n 'report_id' => $automatedReport->getId(),\n 'error' => $e->getMessage(),\n ]);\n $this->release(3600);\n }\n }\n\n /**\n * @return array<UserContract>\n */\n private function resolveUsers(AutomatedReport $automatedReport): array\n {\n if ($automatedReport->isAskJiminnyReport()) {\n $creator = $automatedReport->getCreator();\n\n return $creator !== null ? [$creator] : [];\n }\n\n return $this->automatedReportsService->getRecipientUserObjects($automatedReport);\n }\n\n private function buildPayload(AutomatedReport $automatedReport): array\n {\n return [\n 'report_type' => $automatedReport->getType(),\n 'frequency' => $automatedReport->getFrequency(),\n ];\n }\n\n private function resolveEventName(AutomatedReport $automatedReport): string\n {\n if ($automatedReport->isAskJiminnyReport()) {\n return self::EVENT_NAME_ASK_JIMINNY_REPORT;\n }\n\n return self::EVENT_NAME_AUTOMATED_REPORT;\n }\n}\n\n+++\n\n<?php\n\ndeclare(strict_types=1);\n\nnamespace Tests\\Unit\\Listeners\\AutomatedReports\\UserPilot;\n\nuse GuzzleHttp\\Exception\\GuzzleException;\nuse Jiminny\\Events\\AutomatedReports\\AutomatedReportGenerated;\nuse Jiminny\\Listeners\\AutomatedReports\\UserPilot\\TrackAutomatedReportGeneratedEvent;\nuse Jiminny\\Models\\AutomatedReport;\nuse Jiminny\\Models\\User;\nuse Jiminny\\Services\\Kiosk\\AutomatedReports\\AutomatedReportsService;\nuse Jiminny\\Services\\UserPilot\\UserPilotClient;\nuse PHPUnit\\Framework\\MockObject\\MockObject;\nuse Tests\\TestCase;\n\nclass TrackAutomatedReportGeneratedEventTest extends TestCase\n{\n private UserPilotClient&MockObject $userPilotClient;\n private AutomatedReportsService&MockObject $automatedReportsService;\n\n protected function setUp(): void\n {\n parent::setUp();\n $this->userPilotClient = $this->createMock(UserPilotClient::class);\n $this->automatedReportsService = $this->createMock(AutomatedReportsService::class);\n }\n\n private function makeListener(): TrackAutomatedReportGeneratedEvent\n {\n return new TrackAutomatedReportGeneratedEvent(\n $this->userPilotClient,\n $this->automatedReportsService,\n );\n }\n\n private function makeEvent(AutomatedReport $report): AutomatedReportGenerated\n {\n return new AutomatedReportGenerated($report);\n }\n\n public function testHandleSkipsWhenUserPilotTokenIsNull(): void\n {\n config(['services.userpilot.token' => null]);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->never())->method('isAskJiminnyReport');\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksCreatorForAskJiminnyReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn($creator);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(123);\n\n $this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $creator,\n 'ask-jiminny-report-generated',\n ['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);\n $report->expects($this->once())->method('getCreator')->willReturn(null);\n $report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->expects($this->once())->method('getFrequency')->willReturn('weekly');\n $report->expects($this->once())->method('getId')->willReturn(456);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleTracksAllRecipientsForExecReport(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $userOne = $this->createMock(User::class);\n $userTwo = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(789);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$userOne, $userTwo]);\n\n $this->userPilotClient->expects($this->exactly(2))\n ->method('track')\n ->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {\n $this->assertTrue($user === $userOne || $user === $userTwo);\n $this->assertSame('automated-report-generated', $eventName);\n $this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);\n\n return null;\n });\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('exec_summary');\n $report->expects($this->once())->method('getFrequency')->willReturn('monthly');\n $report->expects($this->once())->method('getId')->willReturn(101);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->willReturn([]);\n\n $this->userPilotClient->expects($this->never())->method('track');\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n\n public function testHandleDoesNotThrowOnGuzzleException(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $creator = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->method('isAskJiminnyReport')->willReturn(true);\n $report->method('getCreator')->willReturn($creator);\n $report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);\n $report->method('getFrequency')->willReturn('daily');\n $report->method('getId')->willReturn(202);\n\n $guzzleException = $this->createMock(GuzzleException::class);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with($creator, 'ask-jiminny-report-generated', $this->anything())\n ->willThrowException($guzzleException);\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n\n $this->addToAssertionCount(1);\n }\n\n public function testHandleTracksAutomatedReportWithSingleRecipient(): void\n {\n config(['services.userpilot.token' => 'NX-token']);\n\n $user = $this->createMock(User::class);\n\n $report = $this->createMock(AutomatedReport::class);\n $report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);\n $report->expects($this->once())->method('getType')->willReturn('team_performance');\n $report->expects($this->once())->method('getFrequency')->willReturn('daily');\n $report->expects($this->once())->method('getId')->willReturn(303);\n\n $this->automatedReportsService->expects($this->once())\n ->method('getRecipientUserObjects')\n ->with($report)\n ->willReturn([$user]);\n\n $this->userPilotClient->expects($this->once())\n ->method('track')\n ->with(\n $user,\n 'automated-report-generated',\n ['report_type' => 'team_performance', 'frequency' => 'daily']\n );\n\n $listener = $this->makeListener();\n $listener->handle($this->makeEvent($report));\n }\n}","role_description":"text entry area","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXStaticText","text":"Project","depth":3,"role_description":"text"},{"role":"AXButton","text":"Project","depth":3,"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"New File or Directory…","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Expand Selected","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Collapse All","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Options","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false},{"role":"AXButton","text":"Hide","depth":4,"bounds":{"left":0.0,"top":0.0,"width":0.018055556,"height":0.026666667},"role_description":"button","is_enabled":true,"is_focused":false,"is_selected":false,"is_expanded":false}]...
|
7506456211656557156
|
8404466858119637786
|
click
|
accessibility
|
NULL
|
2 files committed
JY-20157 move user pilot trackin 2 files committed
JY-20157 move user pilot tracking changes to separated PR
text/html
text/html
text/html
Edit Commit Message…
Project: faVsco.js, menu
#12011 on JY-20157-AJ-report-not-send-notification, menu
Start Listening for PHP Debug Connections
TrackAutomatedReportGeneratedEventTest
Run 'TrackAutomatedReportGeneratedEventTest'
Debug 'TrackAutomatedReportGeneratedEventTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
9
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
43
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\Queue\Constants;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\Contracts\UserContract;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use Illuminate\Support\Facades\Log;
class TrackAutomatedReportGeneratedEvent implements ShouldQueue
{
use InteractsWithQueue;
private const string EVENT_NAME_AUTOMATED_REPORT = 'automated-report-generated';
private const string EVENT_NAME_ASK_JIMINNY_REPORT = 'ask-jiminny-report-generated';
public string $queue = Constants::QUEUE_DELAYABLE;
public function __construct(
private readonly UserPilotClient $userPilotClient,
private readonly AutomatedReportsService $automatedReportsService,
) {
}
public function handle(AutomatedReportGenerated $event): void
{
if (config('services.userpilot.token') === null) {
return;
}
$automatedReport = $event->automatedReport;
$payload = $this->buildPayload($automatedReport);
$eventName = $this->resolveEventName($automatedReport);
$users = $this->resolveUsers($automatedReport);
if (empty($users)) {
Log::warning('[UserPilot] No recipients found for automated report', [
'report_id' => $automatedReport->getId(),
'is_ask_jiminny' => $automatedReport->isAskJiminnyReport(),
]);
return;
}
Log::info('[UserPilot] Sending automated report event', [
'report_id' => $automatedReport->getId(),
'event_name' => $eventName,
'recipient_count' => count($users),
]);
try {
foreach ($users as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
Log::error('[UserPilot] Failed to send automated report event', [
'report_id' => $automatedReport->getId(),
'error' => $e->getMessage(),
]);
$this->release(3600);
}
}
/**
* @return array<UserContract>
*/
private function resolveUsers(AutomatedReport $automatedReport): array
{
if ($automatedReport->isAskJiminnyReport()) {
$creator = $automatedReport->getCreator();
return $creator !== null ? [$creator] : [];
}
return $this->automatedReportsService->getRecipientUserObjects($automatedReport);
}
private function buildPayload(AutomatedReport $automatedReport): array
{
return [
'report_type' => $automatedReport->getType(),
'frequency' => $automatedReport->getFrequency(),
];
}
private function resolveEventName(AutomatedReport $automatedReport): string
{
if ($automatedReport->isAskJiminnyReport()) {
return self::EVENT_NAME_ASK_JIMINNY_REPORT;
}
return self::EVENT_NAME_AUTOMATED_REPORT;
}
}
+++
<?php
declare(strict_types=1);
namespace Tests\Unit\Listeners\AutomatedReports\UserPilot;
use GuzzleHttp\Exception\GuzzleException;
use Jiminny\Events\AutomatedReports\AutomatedReportGenerated;
use Jiminny\Listeners\AutomatedReports\UserPilot\TrackAutomatedReportGeneratedEvent;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\User;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
use PHPUnit\Framework\MockObject\MockObject;
use Tests\TestCase;
class TrackAutomatedReportGeneratedEventTest extends TestCase
{
private UserPilotClient&MockObject $userPilotClient;
private AutomatedReportsService&MockObject $automatedReportsService;
protected function setUp(): void
{
parent::setUp();
$this->userPilotClient = $this->createMock(UserPilotClient::class);
$this->automatedReportsService = $this->createMock(AutomatedReportsService::class);
}
private function makeListener(): TrackAutomatedReportGeneratedEvent
{
return new TrackAutomatedReportGeneratedEvent(
$this->userPilotClient,
$this->automatedReportsService,
);
}
private function makeEvent(AutomatedReport $report): AutomatedReportGenerated
{
return new AutomatedReportGenerated($report);
}
public function testHandleSkipsWhenUserPilotTokenIsNull(): void
{
config(['services.userpilot.token' => null]);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->never())->method('isAskJiminnyReport');
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksCreatorForAskJiminnyReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn($creator);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(123);
$this->automatedReportsService->expects($this->never())->method('getRecipientUserObjects');
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$creator,
'ask-jiminny-report-generated',
['report_type' => AutomatedReportsService::TYPE_ASK_JIMINNY, 'frequency' => 'weekly']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleSkipsTrackingWhenAskJiminnyCreatorIsNull(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(true);
$report->expects($this->once())->method('getCreator')->willReturn(null);
$report->expects($this->once())->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->expects($this->once())->method('getFrequency')->willReturn('weekly');
$report->expects($this->once())->method('getId')->willReturn(456);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleTracksAllRecipientsForExecReport(): void
{
config(['services.userpilot.token' => 'NX-token']);
$userOne = $this->createMock(User::class);
$userTwo = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(789);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$userOne, $userTwo]);
$this->userPilotClient->expects($this->exactly(2))
->method('track')
->willReturnCallback(function ($user, $eventName, $payload) use ($userOne, $userTwo) {
$this->assertTrue($user === $userOne || $user === $userTwo);
$this->assertSame('automated-report-generated', $eventName);
$this->assertSame(['report_type' => 'exec_summary', 'frequency' => 'monthly'], $payload);
return null;
});
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotTrackWhenExecReportHasNoRecipients(): void
{
config(['services.userpilot.token' => 'NX-token']);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(3))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('exec_summary');
$report->expects($this->once())->method('getFrequency')->willReturn('monthly');
$report->expects($this->once())->method('getId')->willReturn(101);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->willReturn([]);
$this->userPilotClient->expects($this->never())->method('track');
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
public function testHandleDoesNotThrowOnGuzzleException(): void
{
config(['services.userpilot.token' => 'NX-token']);
$creator = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->method('isAskJiminnyReport')->willReturn(true);
$report->method('getCreator')->willReturn($creator);
$report->method('getType')->willReturn(AutomatedReportsService::TYPE_ASK_JIMINNY);
$report->method('getFrequency')->willReturn('daily');
$report->method('getId')->willReturn(202);
$guzzleException = $this->createMock(GuzzleException::class);
$this->userPilotClient->expects($this->once())
->method('track')
->with($creator, 'ask-jiminny-report-generated', $this->anything())
->willThrowException($guzzleException);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
$this->addToAssertionCount(1);
}
public function testHandleTracksAutomatedReportWithSingleRecipient(): void
{
config(['services.userpilot.token' => 'NX-token']);
$user = $this->createMock(User::class);
$report = $this->createMock(AutomatedReport::class);
$report->expects($this->exactly(2))->method('isAskJiminnyReport')->willReturn(false);
$report->expects($this->once())->method('getType')->willReturn('team_performance');
$report->expects($this->once())->method('getFrequency')->willReturn('daily');
$report->expects($this->once())->method('getId')->willReturn(303);
$this->automatedReportsService->expects($this->once())
->method('getRecipientUserObjects')
->with($report)
->willReturn([$user]);
$this->userPilotClient->expects($this->once())
->method('track')
->with(
$user,
'automated-report-generated',
['report_type' => 'team_performance', 'frequency' => 'daily']
);
$listener = $this->makeListener();
$listener->handle($this->makeEvent($report));
}
}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
NULL
|