|
75388
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
10
92
69
Previous Highlighted Error
Next Highlighted Error
FirefoxFileProfilesTools• Daily - Platform • in 22 m100% <78Fri 24 Apr 9:23:50EditViewHistory→BookmarksWindowHelpapp.circleci.com/pipelines/github/jiminny/app/57688/workflows/c1b289da-08aa-4aad-b2a7-f2de3bc6749a/jobs/876 Wcircleci •New TabAl reports promotion pages by nikcJY-9712 | Nuges to expire after onJiminnyUserpilot | Logged-activityJY-20157 add not enough activitie:test (876494) - jiminny/app+ New TabAll Pipelines / 0test (876494)app / g JY-20157-AJ-report-not-send-notification / C app #57688 / build_accept_deploy /HomeCache .env filePipelinesRun PHPunit tests1s7m 14sProjectsFix error (Beta)Explain errorDeploysInsightsRunners•.•MoreChunkLộ3#!/bin/bash -eo pipefailexport SYMFONY_DEPRECATIONS_HELPER=weaKecho -e "\nxdebug-mode=coverage\nxdebug.coverage_output_dir=/home/circleci/project\nzend_extension=xdebug.so\n" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.inivendor/bin/paratest --coverage-clover coverage.xml --log-junit execution.xml ) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayLoadWithMonthlyFrequencyFailed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11" ./home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:2263) Tests\Unit\Services \Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequencyFailed asserting that two strings are equal.--- Expected+++ Actual- '2023-02-08 00:00:00'+'2023-02-0500:00:00'/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75388
|
|
75389
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
10
92
69
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Support\Carbon as IlluminateCarbon;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Component\AskAnything\Dtos\AskAnythingPromptDto;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Repositories\UserRepository;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Illuminate\Support\Collection;
use Jiminny\Models\AskAnything\AskAnythingPrompt;
use Jiminny\Models\AskAnything\AskAnythingPromptTarget;
use Jiminny\Models\Activity\Search;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Group;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AskAnythingRepository;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Mockery;
use PHPUnit\Framework\Attributes\DataProvider;
use Tests\TestCase;
class AutomatedReportsServiceTest extends TestCase
{
private AutomatedReportsService $service;
protected function setUp(): void
{
parent::setUp();
// Create a real instance of the service without calling the constructor
$reflection = new \ReflectionClass(AutomatedReportsService::class);
$this->service = $reflection->newInstanceWithoutConstructor();
// Manually set the dependencies using reflection
$dependencies = [
'teamRepository' => TeamRepository::class,
'groupRepository' => GroupRepository::class,
'userRepository' => UserRepository::class,
'stageRepository' => StageRepository::class,
'dealStagesService' => DealStagesService::class,
'recipientsService' => RecipientsService::class,
'automatedReportsRepository' => AutomatedReportsRepository::class,
'webhookService' => Webhook::class,
'dispatcher' => Dispatcher::class,
'activityTypeService' => ActivityTypeService::class,
'playbookCategoryRepository' => PlaybookCategoryRepository::class,
'askAnythingPromptService' => AskAnythingPromptService::class,
'activitySearchRepository' => SearchRepository::class,
'askAnythingRepository' => AskAnythingRepository::class,
];
foreach ($dependencies as $propertyName => $class) {
$property = $reflection->getProperty($propertyName);
$property->setAccessible(true);
$property->setValue($this->service, $this->createMock($class));
}
}
protected function tearDown(): void
{
parent::tearDown();
Mockery::close();
}
private function getService(
$mockUserRepository = null,
$mockStageRepository = null,
$mockTeamRepository = null,
): AutomatedReportsService {
return new AutomatedReportsService(
($mockTeamRepository ?? $this->createMock(TeamRepository::class)),
$this->createMock(GroupRepository::class),
($mockUserRepository ?? $this->createMock(UserRepository::class)),
($mockStageRepository ?? $this->createMock(StageRepository::class)),
$this->createMock(DealStagesService::class),
$this->createMock(RecipientsService::class),
$this->createMock(AutomatedReportsRepository::class),
$this->createMock(Webhook::class),
$this->createMock(Dispatcher::class),
$this->createMock(ActivityTypeService::class),
$this->createMock(PlaybookCategoryRepository::class),
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
#[DataProvider('transformMediaTypesDataProvider')]
public function testTransformMediaTypes(array $mediaTypes, array $expected): void
{
$report = new AutomatedReport(['media_types' => $mediaTypes]);
$reflection = new \ReflectionClass(AutomatedReportsService::class);
$method = $reflection->getMethod('transformMediaTypes');
$result = $method->invoke($this->service, $report);
$this->assertEquals($expected, $result);
}
public function testGetMediaTypeFieldDataWithoutReport(): void
{
$result = $this->service->getMediaTypeFieldData(null);
$this->assertIsArray($result);
$this->assertArrayHasKey('value', $result);
$this->assertEmpty($result['value']);
$this->assertEquals('media_types', $result['id']);
}
public function testGetMediaTypeFieldDataWithReport(): void
{
$mediaTypes = ['pdf', 'podcast'];
$report = new AutomatedReport(['media_types' => $mediaTypes]);
$result = $this->service->getMediaTypeFieldData($report);
$expectedValue = [
['id' => 'pdf', 'name' => 'PDF'],
['id' => 'podcast', 'name' => 'Podcast'],
];
$this->assertIsArray($result);
$this->assertArrayHasKey('value', $result);
$this->assertEquals($expectedValue, $result['value']);
}
public static function transformMediaTypesDataProvider(): array
{
return [
'empty array' => [
'mediaTypes' => [],
'expected' => [],
],
'pdf only' => [
'mediaTypes' => ['pdf'],
'expected' => [
['id' => 'pdf', 'name' => 'PDF'],
],
],
'podcast only' => [
'mediaTypes' => ['podcast'],
'expected' => [
['id' => 'podcast', 'name' => 'Podcast'],
],
],
'both pdf and podcast' => [
'mediaTypes' => ['pdf', 'podcast'],
'expected' => [
['id' => 'pdf', 'name' => 'PDF'],
['id' => 'podcast', 'name' => 'Podcast'],
],
],
'with invalid type' => [
'mediaTypes' => ['pdf', 'invalid', 'podcast'],
'expected' => [
['id' => 'pdf', 'name' => 'PDF'],
['id' => 'podcast', 'name' => 'Podcast'],
],
],
];
}
#[DataProvider('hasCallTypeConferenceDataProvider')]
public function testHasCallTypeConference(array $callTypes, bool $expected): void
{
$report = $this->createMock(AutomatedReport::class);
$report->method('getCallTypes')->willReturn($callTypes);
$result = $this->service->hasCallTypeConference($report);
$this->assertEquals($expected, $result);
}
#[DataProvider('hasCallTypeDialerDataProvider')]
public function testHasCallTypeDialer(array $callTypes, bool $expected): void
{
$report = $this->createMock(AutomatedReport::class);
$report->method('getCallTypes')->willReturn($callTypes);
$result = $this->service->hasCallTypeDialer($report);
$this->assertEquals($expected, $result);
}
public static function hasCallTypeConferenceDataProvider(): array
{
return [
'has conference' => [
'callTypes' => ['conference', 'dialer'],
'expected' => true,
],
'does not have conference' => [
'callTypes' => ['dialer', 'other'],
'expected' => false,
],
'empty call types' => [
'callTypes' => [],
'expected' => false,
],
];
}
public static function hasCallTypeDialerDataProvider(): array
{
return [
'has dialer' => [
'callTypes' => ['conference', 'dialer'],
'expected' => true,
],
'does not have dialer' => [
'callTypes' => ['conference', 'other'],
'expected' => false,
],
'empty call types' => [
'callTypes' => [],
'expected' => false,
],
];
}
public function testTransformReportResultsWithEmptyCollection(): void
{
$emptyCollection = new Collection([]);
$result = $this->service->transformReportResults($emptyCollection);
$this->assertIsArray($result);
$this->assertEmpty($result);
}
public function testTransformReportResultsStructure(): void
{
// Create a mock AutomatedReportResult with minimal setup to test structure
$mockReportResult = $this->createMockReportResult();
$collection = new Collection([$mockReportResult]);
$result = $this->service->transformReportResults($collection);
$this->assertIsArray($result);
$this->assertCount(1, $result);
$transformedResult = $result[0];
// Verify all expected keys are present
$expectedKeys = [
'id', 'name', 'frequency', 'recipients',
'report_type', 'media_type', 'downloadUrl', 'viewUrl', 'generated_at',
];
foreach ($expectedKeys as $key) {
$this->assertArrayHasKey($key, $transformedResult);
}
// Verify structure of nested arrays
$this->assertIsArray($transformedResult['frequency']);
$this->assertArrayHasKey('id', $transformedResult['frequency']);
$this->assertArrayHasKey('name', $transformedResult['frequency']);
$this->assertIsArray($transformedResult['report_type']);
$this->assertArrayHasKey('id', $transformedResult['report_type']);
$this->assertArrayHasKey('name', $transformedResult['report_type']);
$this->assertIsArray($transformedResult['recipients']);
// Verify TODO fields are null as expected
$this->assertEquals(AutomatedReportsService::MEDIA_TYPE_PODCAST, $transformedResult['media_type']);
$this->assertEquals(route('ai-reports.audio.download', ['uuid' => 'test-uuid']), $transformedResult['downloadUrl']);
$this->assertEquals(route('ai-reports.audio.view', ['uuid' => 'test-uuid']), $transformedResult['viewUrl']);
}
public function testTransformReportResultsWithMultipleResults(): void
{
$mockReportResult1 = $this->createMockReportResult('result-uuid-1', 'exec_summary');
$mockReportResult2 = $this->createMockReportResult('result-uuid-2', 'coaching_profiles');
$collection = new Collection([$mockReportResult1, $mockReportResult2]);
$result = $this->service->transformReportResults($collection);
$this->assertIsArray($result);
$this->assertCount(2, $result);
// Verify different UUIDs
$this->assertEquals('result-uuid-1', $result[0]['id']);
$this->assertEquals('result-uuid-2', $result[1]['id']);
// Verify both results have the expected structure
foreach ($result as $transformedResult) {
$this->assertArrayHasKey('id', $transformedResult);
$this->assertArrayHasKey('name', $transformedResult);
$this->assertArrayHasKey('frequency', $transformedResult);
$this->assertArrayHasKey('recipients', $transformedResult);
$this->assertArrayHasKey('report_type', $transformedResult);
}
}
#[DataProvider('isUserRecipientOfReportDataProvider')]
public function testIsUserRecipientOfReport(int $userId, array $recipients, bool $expected): void
{
// Create mock User
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn($userId);
$mockUser->method('getGroupId')->willReturn(null);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn($recipients);
$mockReport->method('isAskJiminnyReport')->willReturn(false);
$mockReport->method('getGroups')->willReturn([]);
$result = $this->service->isUserRecipientOfReport($mockUser, $mockReport);
$this->assertEquals($expected, $result);
}
#[DataProvider('isUserRecipientOfAskJiminnyReportDataProvider')]
public function testIsUserRecipientOfAskJiminnyReportViaGroup(
int $userId,
?int $groupId,
array $recipients,
array $reportGroups,
bool $expected,
): void {
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn($userId);
$mockUser->method('getGroupId')->willReturn($groupId);
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn($recipients);
$mockReport->method('isAskJiminnyReport')->willReturn(true);
$mockReport->method('getGroups')->willReturn($reportGroups);
$this->assertSame($expected, $this->service->isUserRecipientOfReport($mockUser, $mockReport));
}
public function testIsUserRecipientOfNonAskJiminnyReportIgnoresGroups(): void
{
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn(123);
$mockUser->method('getGroupId')->willReturn(5);
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['users' => []]);
$mockReport->method('isAskJiminnyReport')->willReturn(false);
$mockReport->method('getGroups')->willReturn([5]);
$this->assertFalse($this->service->isUserRecipientOfReport($mockUser, $mockReport));
}
public static function isUserRecipientOfAskJiminnyReportDataProvider(): array
{
return [
'group member - ask jiminny' => [
'userId' => 123,
'groupId' => 7,
'recipients' => ['users' => []],
'reportGroups' => [7],
'expected' => true,
],
'group mismatch - ask jiminny' => [
'userId' => 123,
'groupId' => 9,
'recipients' => ['users' => []],
'reportGroups' => [7, 8],
'expected' => false,
],
'user with no group - ask jiminny' => [
'userId' => 123,
'groupId' => null,
'recipients' => ['users' => []],
'reportGroups' => [7],
'expected' => false,
],
'recipient users take precedence over group' => [
'userId' => 123,
'groupId' => null,
'recipients' => ['users' => [123]],
'reportGroups' => [],
'expected' => true,
],
];
}
public function testIsUserRecipientOfReportWithEmptyRecipients(): void
{
// Create mock User
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn(123);
// Create mock AutomatedReport with no recipients
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn([]);
$result = $this->service->isUserRecipientOfReport($mockUser, $mockReport);
$this->assertFalse($result);
}
public function testIsUserRecipientOfReportWithNoUsersKey(): void
{
// Create mock User
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn(123);
// Create mock AutomatedReport with recipients but no 'users' key
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['other_key' => [456, 789]]);
$result = $this->service->isUserRecipientOfReport($mockUser, $mockReport);
$this->assertFalse($result);
}
public static function isUserRecipientOfReportDataProvider(): array
{
return [
'user is recipient - single user' => [
'userId' => 123,
'recipients' => ['users' => [123]],
'expected' => true,
],
'user is recipient - multiple users' => [
'userId' => 456,
'recipients' => ['users' => [123, 456, 789]],
'expected' => true,
],
'user is not recipient - single user' => [
'userId' => 999,
'recipients' => ['users' => [123]],
'expected' => false,
],
'user is not recipient - multiple users' => [
'userId' => 999,
'recipients' => ['users' => [123, 456, 789]],
'expected' => false,
],
'user is recipient - string IDs converted to int' => [
'userId' => 123,
'recipients' => ['users' => ['123', '456']],
'expected' => true,
],
'user is not recipient - string IDs converted to int' => [
'userId' => 999,
'recipients' => ['users' => ['123', '456']],
'expected' => false,
],
'empty users array' => [
'userId' => 123,
'recipients' => ['users' => []],
'expected' => false,
],
];
}
private function createMockReportResult(string $uuid = 'test-uuid', string $reportType = 'exec_summary'): AutomatedReportResult
{
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getFrequency')->willReturn('weekly');
$mockReport->method('getRecipients')->willReturn(['users' => [1, 2]]);
$mockReport->method('getGroups')->willReturn([10, 20]);
$mockReport->method('getType')->willReturn($reportType);
// Create mock Team
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
// Create mock Group
$mockGroup = $this->createMock(\Jiminny\Models\Group::class);
$mockGroup->method('getUuid')->willReturn('group-uuid-10');
$mockGroup->method('getName')->willReturn('Test Team');
$mockQueryBuilder = Mockery::mock();
$mockQueryBuilder->shouldReceive('where')->andReturnSelf();
$mockQueryBuilder->shouldReceive('first')->andReturn($mockGroup);
$dataRelation = Mockery::mock(HasMany::class);
$dataRelation->shouldReceive('where')->andReturn($mockQueryBuilder);
$dataRelation->shouldReceive('get')->andReturn(
new \Illuminate\Database\Eloquent\Collection([$mockGroup])
);
$mockTeam->method('groups')->willReturn($dataRelation);
$mockReport->method('getTeam')->willReturn($mockTeam);
// Create mock AutomatedReportResult
$mockReportResult = $this->createMock(AutomatedReportResult::class);
$mockReportResult->method('getUuid')->willReturn($uuid);
$mockReportResult->method('getGeneratedAt')->willReturn(
\Illuminate\Support\Carbon::parse('2024-01-15T10:30:00Z')
);
$mockReportResult->method('getReport')->willReturn($mockReport);
// Mock methods used in getReportFileName
$mockReportResult->method('getReportType')->willReturn($reportType);
$mockReportResult->method('getFromDate')->willReturn(
\Illuminate\Support\Carbon::parse('2024-01-08')
);
$mockReportResult->method('getToDate')->willReturn(
\Illuminate\Support\Carbon::parse('2024-01-15')
);
$mockReportResult->method('getGroups')->willReturn([10]);
$mockReportResult->method('getMediaType')->willReturn(AutomatedReportsService::MEDIA_TYPE_PODCAST);
return $mockReportResult;
}
#[DataProvider('getUsersUuidsDataProvider')]
public function testGetUsersUuids(array $recipients, array $mockUsers, array $expectedUuids): void
{
// Create mock UserRepository
$mockUserRepository = $this->createMock(UserRepository::class);
// Configure the mock to return specific users for specific IDs using a callback
$mockUserRepository->method('find')
->willReturnCallback(function ($userId) use ($mockUsers) {
if (! isset($mockUsers[$userId])) {
return null;
}
$userUuid = $mockUsers[$userId]['uuid'] ?? null;
if ($userUuid === null) {
return null;
}
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getUuid')->willReturn((string) $userUuid);
return $mockUser;
});
// Create service with mocked UserRepository
$automatedReportsService = $this->getService(mockUserRepository: $mockUserRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn($recipients);
$result = $automatedReportsService->getUsersUuids($mockReport);
$this->assertEquals($expectedUuids, $result);
}
public function testGetUsersUuidsWithEmptyRecipients(): void
{
// Create mock AutomatedReport with empty recipients
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn([]);
$result = $this->service->getUsersUuids($mockReport);
$this->assertEquals([], $result);
}
public function testGetUsersUuidsWithNoUsersKey(): void
{
// Create mock AutomatedReport with recipients but no 'users' key
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['other_key' => [1, 2, 3]]);
$result = $this->service->getUsersUuids($mockReport);
$this->assertEquals([], $result);
}
public function testGetUsersUuidsWithNonExistentUsers(): void
{
// Create mock UserRepository that returns null for all users
$mockUserRepository = $this->createMock(UserRepository::class);
$mockUserRepository->method('find')->willReturn(null);
// Create service with mocked UserRepository
$automatedReportsService = $this->getService(mockUserRepository: $mockUserRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['users' => [1, 2, 3]]);
$result = $automatedReportsService->getUsersUuids($mockReport);
// Should return array with null values for non-existent users
$this->assertEquals([], $result);
}
public static function getUsersUuidsDataProvider(): array
{
return [
'single user found' => [
'recipients' => ['users' => [123]],
'mockUsers' => [
123 => ['id' => 123, 'uuid' => 'user-uuid-123'],
],
'expectedUuids' => ['user-uuid-123'],
],
'multiple users found' => [
'recipients' => ['users' => [123, 456, 789]],
'mockUsers' => [
123 => ['id' => 123, 'uuid' => 'user-uuid-123'],
456 => ['id' => 456, 'uuid' => 'user-uuid-456'],
789 => ['id' => 789, 'uuid' => 'user-uuid-789'],
],
'expectedUuids' => ['user-uuid-123', 'user-uuid-456', 'user-uuid-789'],
],
'mixed found and not found users' => [
'recipients' => ['users' => [123, 456, 789]],
'mockUsers' => [
123 => ['id' => 123, 'uuid' => 'user-uuid-123'],
// 456 not found in DB
789 => ['id' => 789, 'uuid' => 'user-uuid-789'],
],
'expectedUuids' => ['user-uuid-123', 'user-uuid-789'], // Updated to reflect that nulls are filtered out
],
'empty users array' => [
'recipients' => ['users' => []],
'mockUsers' => [],
'expectedUuids' => [],
],
'all users not found' => [
'recipients' => ['users' => [123, 456]],
'mockUsers' => [], // No users found
'expectedUuids' => [], // Updated to reflect that nulls are filtered out
],
];
}
#[DataProvider('getCurrentDealStagesUuidsDataProvider')]
public function testGetCurrentDealStagesUuids(array $currentDealStages, array $mockStages, array $expectedUuids): void
{
// Create mock StageRepository
$mockStageRepository = $this->createMock(StageRepository::class);
// Configure the mock to return specific stages for specific IDs using a callback
$mockStageRepository->method('find')
->willReturnCallback(function ($stageId) use ($mockStages) {
if (! isset($mockStages[$stageId])) {
return null;
}
$stageUuid = $mockStages[$stageId]['uuid'] ?? null;
if ($stageUuid === null) {
return null;
}
$mockStage = $this->createMock(\Jiminny\Models\Stage::class);
$mockStage->method('getUuid')->willReturn((string) $stageUuid);
return $mockStage;
});
// Create service with mocked StageRepository
$automatedReportsService = $this->getService(mockStageRepository: $mockStageRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getCurrentDealStages')->willReturn($currentDealStages);
$result = $automatedReportsService->getCurrentDealStagesUuids($mockReport);
$this->assertEquals($expectedUuids, $result);
}
public function testGetCurrentDealStagesUuidsWithEmptyStages(): void
{
// Create mock AutomatedReport with empty current deal stages
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getCurrentDealStages')->willReturn([]);
$result = $this->service->getCurrentDealStagesUuids($mockReport);
$this->assertEquals([], $result);
}
public function testGetCurrentDealStagesUuidsWithNonExistentStages(): void
{
// Create mock StageRepository that returns null for all stages
$mockStageRepository = $this->createMock(StageRepository::class);
$mockStageRepository->method('find')->willReturn(null);
// Create service with mocked StageRepository
$automatedReportsService = $this->getService(mockStageRepository: $mockStageRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getCurrentDealStages')->willReturn([1, 2, 3]);
$result = $automatedReportsService->getCurrentDealStagesUuids($mockReport);
// Should return array with null values for non-existent stages
$this->assertEquals([], $result);
}
public static function getCurrentDealStagesUuidsDataProvider(): array
{
return [
'single stage found' => [
'currentDealStages' => [10],
'mockStages' => [
10 => ['id' => 10, 'uuid' => 'stage-uuid-10'],
],
'expectedUuids' => ['stage-uuid-10'],
],
'multiple stages found' => [
'currentDealStages' => [10, 20, 30],
'mockStages' => [
10 => ['id' => 10, 'uuid' => 'stage-uuid-10'],
20 => ['id' => 20, 'uuid' => 'stage-uuid-20'],
30 => ['id' => 30, 'uuid' => 'stage-uuid-30'],
],
'expectedUuids' => ['stage-uuid-10', 'stage-uuid-20', 'stage-uuid-30'],
],
'mixed found and not found stages' => [
'currentDealStages' => [10, 20, 30],
'mockStages' => [
10 => ['id' => 10, 'uuid' => 'stage-uuid-10'],
// 20 not found in DB
30 => ['id' => 30, 'uuid' => 'stage-uuid-30'],
],
'expectedUuids' => ['stage-uuid-10', 'stage-uuid-30'], // Updated to reflect that nulls are filtered out
],
'empty stages array' => [
'currentDealStages' => [],
'mockStages' => [],
'expectedUuids' => [],
],
'all stages not found' => [
'currentDealStages' => [10, 20],
'mockStages' => [], // No stages found
'expectedUuids' => [], // Updated to reflect that nulls are filtered out
],
];
}
#[DataProvider('getTeamGroupsDataProvider')]
public function testGetTeamGroups(string $teamUuid, ?array $mockTeamData, array $mockGroups, array $expectedResult): void
{
// Create mock TeamRepository
$mockTeamRepository = $this->createMock(TeamRepository::class);
if ($mockTeamData === null) {
// Team not found
$mockTeamRepository->method('idOrUuid')
->with($teamUuid)
->willReturn(null);
} else {
// Team found - create mock team with groups
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
// Create mock groups collection
$mockGroupsCollection = $this->createMock(\Illuminate\Database\Eloquent\Collection::class);
// Create mock Group objects
$groupObjects = [];
foreach ($mockGroups as $groupData) {
$mockGroup = $this->createMock(\Jiminny\Models\Group::class);
$mockGroup->method('getUuid')->willReturn($groupData['id']);
$mockGroup->method('getName')->willReturn($groupData['name']);
$groupObjects[] = $mockGroup;
}
// Mock the groups collection to return our mock groups
$mockGroupsCollection->method('getIterator')->willReturn(new \ArrayIterator($groupObjects));
// Mock the groups() relation
$mockGroupsRelation = $this->createMock(\Illuminate\Database\Eloquent\Relations\HasMany::class);
$mockGroupsRelation->method('get')->willReturn($mockGroupsCollection);
$mockTeam->method('groups')->willReturn($mockGroupsRelation);
$mockTeamRepository->method('idOrUuid')
->with($teamUuid)
->willReturn($mockTeam);
}
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeamGroups($teamUuid);
$this->assertEquals($expectedResult, $result);
}
public function testGetTeamGroupsWithNonExistentTeam(): void
{
// Create mock TeamRepository that returns null (team not found)
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('idOrUuid')->willReturn(null);
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeamGroups('non-existent-team-uuid');
$this->assertEquals([], $result);
}
public function testGetTeamGroupsWithEmptyGroups(): void
{
// Create mock team with no groups
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
// Create empty groups collection
$mockGroupsCollection = $this->createMock(\Illuminate\Database\Eloquent\Collection::class);
$mockGroupsCollection->method('getIterator')->willReturn(new \ArrayIterator([]));
$mockGroupsRelation = $this->createMock(\Illuminate\Database\Eloquent\Relations\HasMany::class);
$mockGroupsRelation->method('get')->willReturn($mockGroupsCollection);
$mockTeam->method('groups')->willReturn($mockGroupsRelation);
// Create mock TeamRepository
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('idOrUuid')->willReturn($mockTeam);
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeamGroups('team-with-no-groups');
$this->assertEquals([], $result);
}
public static function getTeamGroupsDataProvider(): array
{
return [
'team with single group' => [
'teamUuid' => 'team-uuid-123',
'mockTeamData' => ['id' => 'team-uuid-123', 'name' => 'Test Team'],
'mockGroups' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
],
'expectedResult' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
],
],
'team with multiple groups' => [
'teamUuid' => 'team-uuid-456',
'mockTeamData' => ['id' => 'team-uuid-456', 'name' => 'Another Team'],
'mockGroups' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
['id' => 'group-uuid-2', 'name' => 'Marketing Team'],
['id' => 'group-uuid-3', 'name' => 'Support Team'],
],
'expectedResult' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
['id' => 'group-uuid-2', 'name' => 'Marketing Team'],
['id' => 'group-uuid-3', 'name' => 'Support Team'],
],
],
'team not found' => [
'teamUuid' => 'non-existent-uuid',
'mockTeamData' => null,
'mockGroups' => [],
'expectedResult' => [],
],
'team with no groups' => [
'teamUuid' => 'team-uuid-empty',
'mockTeamData' => ['id' => 'team-uuid-empty', 'name' => 'Empty Team'],
'mockGroups' => [],
'expectedResult' => [],
],
];
}
#[DataProvider('getTeamsDataProvider')]
public function testGetTeams(array $mockTeams, array $expectedResult): void
{
// Create mock TeamRepository
$mockTeamRepository = $this->createMock(TeamRepository::class);
// Create mock Team objects
$teamObjects = [];
foreach ($mockTeams as $teamData) {
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
$mockTeam->method('getUuid')->willReturn($teamData['id']);
$mockTeam->method('getName')->willReturn($teamData['name']);
$mockTeam->method('hasFeature')
->with(\Jiminny\Models\Feature\FeatureEnum::AUTOMATED_REPORTS)
->willReturn($teamData['hasAutomatedReports']);
$teamObjects[] = $mockTeam;
}
// Mock the repository to return a Collection (not array)
$mockTeamRepository->method('getTeamsForKiosk')
->with('active')
->willReturn(new Collection($teamObjects));
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeams();
$this->assertEquals($expectedResult, $result);
}
public function testGetTeamsWithNoTeams(): void
{
// Create mock TeamRepository that returns empty Collection
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('getTeamsForKiosk')->willReturn(new Collection([]));
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeams();
$this->assertEquals([], $result);
}
public function testGetTeamsWithAllTeamsWithoutFeature(): void
{
// Create mock teams without AUTOMATED_REPORTS feature
$mockTeam1 = $this->createMock(\Jiminny\Models\Team::class);
$mockTeam1->method('hasFeature')
->with(\Jiminny\Models\Feature\FeatureEnum::AUTOMATED_REPORTS)
->willReturn(false);
$mockTeam2 = $this->createMock(\Jiminny\Models\Team::class);
$mockTeam2->method('hasFeature')
->with(\Jiminny\Models\Feature\FeatureEnum::AUTOMATED_REPORTS)
->willReturn(false);
// Create mock TeamRepository that returns Collection
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('getTeamsForKiosk')->willReturn(new Collection([$mockTeam1, $mockTeam2]));
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeams();
$this->assertEquals([], $result);
}
public static function getTeamsDataProvider(): array
{
return [
'single team with feature' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => true,
],
],
'expectedResult' => [
['id' => 'team-uuid-1', 'name' => 'Sales Team'],
],
],
'multiple teams with feature' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => true,
],
[
'id' => 'team-uuid-2',
'name' => 'Marketing Team',
'hasAutomatedReports' => true,
],
[
'id' => 'team-uuid-3',
'name' => 'Support Team',
'hasAutomatedReports' => true,
],
],
'expectedResult' => [
['id' => 'team-uuid-1', 'name' => 'Sales Team'],
['id' => 'team-uuid-2', 'name' => 'Marketing Team'],
['id' => 'team-uuid-3', 'name' => 'Support Team'],
],
],
'mixed teams - some with feature, some without' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => true,
],
[
'id' => 'team-uuid-2',
'name' => 'Marketing Team',
'hasAutomatedReports' => false,
],
[
'id' => 'team-uuid-3',
'name' => 'Support Team',
'hasAutomatedReports' => true,
],
],
'expectedResult' => [
['id' => 'team-uuid-1', 'name' => 'Sales Team'],
['id' => 'team-uuid-3', 'name' => 'Support Team'],
],
],
'all teams without feature' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => false,
],
[
'id' => 'team-uuid-2',
'name' => 'Marketing Team',
'hasAutomatedReports' => false,
],
],
'expectedResult' => [],
],
'empty teams array' => [
'mockTeams' => [],
'expectedResult' => [],
],
];
}
#[DataProvider('deleteS3FilesDataProvider')]
public function testDeleteS3Files(
string $mediaType,
array $expectedFileExtensions,
array $existingFiles,
string $pathSuffix,
int $expectedDeletes
): void {
// Arrange
$teamUuid = 'team-uuid-123';
$reportUuid = 'report-uuid-456';
$basePath = sprintf('%s/reports/%s', $teamUuid, $reportUuid);
$team = Mockery::mock(Team::class);
$team->allows('getUuid')->andReturn($teamUuid);
$report = Mockery::mock(AutomatedReport::class);
$report->allows('getTeam')->andReturn($team);
$result = Mockery::mock(AutomatedReportResult::class);
$result->allows('getReport')->andReturn($report);
$result->allows('getUuid')->andReturn($reportUuid);
$result->allows('getMediaType')->andReturn($mediaType);
Storage::fake();
Log::shouldReceive('info')->times($expectedDeletes);
foreach ($existingFiles as $extension) {
$filePath = $basePath . $pathSuffix . '.' . $extension;
Storage::put($filePath, 'dummy content');
}
// Act
$this->service->deleteS3Files($result);
// Assert
foreach ($expectedFileExtensions as $extension) {
$filePath = $basePath . $pathSuffix . '.' . $extension;
if (in_array($extension, $existingFiles, true)) {
Storage::assertMissing($filePath);
} else {
// To be sure no unexpected files were created and deleted
Storage::assertMissing($filePath);
}
}
}
public static function deleteS3FilesDataProvider(): array
{
return [
'PDF report, all files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PDF,
'expectedFileExtensions' => ['html', 'MD', 'pdf'],
'existingFiles' => ['html', 'MD', 'pdf'],
'pathSuffix' => '',
'expectedDeletes' => 3,
],
'PDF report, some files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PDF,
'expectedFileExtensions' => ['html', 'MD', 'pdf'],
'existingFiles' => ['html', 'pdf'],
'pathSuffix' => '',
'expectedDeletes' => 2,
],
'PDF report, no files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PDF,
'expectedFileExtensions' => ['html', 'MD', 'pdf'],
'existingFiles' => [],
'pathSuffix' => '',
'expectedDeletes' => 0,
],
'Podcast report, all files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'expectedFileExtensions' => ['json', 'mp3', 'ssml'],
'existingFiles' => ['json', 'mp3', 'ssml'],
'pathSuffix' => '_podcast',
'expectedDeletes' => 3,
],
'Podcast report, some files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'expectedFileExtensions' => ['json', 'mp3', 'ssml'],
'existingFiles' => ['mp3'],
'pathSuffix' => '_podcast',
'expectedDeletes' => 1,
],
'Podcast report, no files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'expectedFileExtensions' => ['json', 'mp3', 'ssml'],
'existingFiles' => [],
'pathSuffix' => '_podcast',
'expectedDeletes' => 0,
],
'Other media type, should do nothing' => [
'mediaType' => 'some_other_type',
'expectedFileExtensions' => [],
'existingFiles' => [],
'pathSuffix' => '',
'expectedDeletes' => 0,
],
];
}
public function testDeleteReportsResultsInRetentionPeriodWithLogging(): void
{
// Create mocks for the test
$automatedReportsService = Mockery::mock(AutomatedReportsService::class);
$team = Mockery::mock(Team::class);
$team->shouldReceive('getId')->andReturn(123);
$from = now()->subDays(30);
$to = now();
$source = 'test-source';
// Expect the method to be called with specific parameters
$automatedReportsService->shouldReceive('deleteReportsResultsInRetentionPeriodWithLogging')
->once()
->with(
$team,
Mockery::on(function ($arg) use ($from) {
return $arg->timestamp === $from->timestamp;
}),
Mockery::on(function ($arg) use ($to) {
return $arg->timestamp === $to->timestamp;
}),
$source
)
->andReturn(5);
// Call the method and verify the result
$result = $automatedReportsService->deleteReportsResultsInRetentionPeriodWithLogging(
$team,
$from,
$to,
$source
);
$this->assertEquals(5, $result);
}
#[DataProvider('sanitizeFileNameDataProvider')]
public function testSanitizeFileName(string $input, string $expected): void
{
$result = $this->service->sanitizeFileName($input);
$this->assertEquals($expected, $result);
}
public static function sanitizeFileNameDataProvider(): array
{
return [
'no special characters' => [
'input' => 'Exec Summary - Sep 2025 - Business Development Team',
'expected' => 'Exec Summary - Sep 2025 - Business Development Team',
],
'forward slash in team name' => [
'input' => 'Exec Summary - Sep 2025 - ND/IRV',
'expected' => 'Exec Summary - Sep 2025 - ND-IRV',
],
'backslash in team name' => [
'input' => 'Exec Summary - Sep 2025 - ND\IRV',
'expected' => 'Exec Summary - Sep 2025 - ND-IRV',
],
'multiple forward slashes' => [
'input' => 'Report - Team A/B/C',
'expected' => 'Report - Team A-B-C',
],
'multiple backslashes' => [
'input' => 'Report - Team A\B\C',
'expected' => 'Report - Team A-B-C',
],
'mixed slashes and backslashes' => [
'input' => 'Report - Team A/B\C',
'expected' => 'Report - Team A-B-C',
],
'complex team name with slashes' => [
'input' => 'Exec Summary - Sep 2025 - Business Development Team - ND/IRV, Net Driven - Acquisition (Sales)',
'expected' => 'Exec Summary - Sep 2025 - Business Development Team - ND-IRV, Net Driven - Acquisition (Sales)',
],
'only slashes' => [
'input' => '//\\\\',
'expected' => '----',
],
'empty string' => [
'input' => '',
'expected' => '',
],
'slash at start' => [
'input' => '/Report Name',
'expected' => '-Report Name',
],
'slash at end' => [
'input' => 'Report Name/',
'expected' => 'Report Name-',
],
];
}
public function testGetReportFileNameSanitizesOutput(): void
{
// Create mock GroupRepository
$mockGroupRepository = $this->createMock(GroupRepository::class);
// Create mock Group with slash in name
$mockGroup = $this->createMock(\Jiminny\Models\Group::class);
$mockGroup->method('getName')->willReturn('ND/IRV, Net Driven - Acquisition (Sales)');
$mockGroupRepository->method('find')->willReturn($mockGroup);
// Create service with mocked GroupRepository
$service = new AutomatedReportsService(
$this-...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75389
|
|
75390
|
DMSActivityMorePhpStormcaltViewINavigareJiminny .. DMSActivityMorePhpStormcaltViewINavigareJiminny ...v# c-learning-peopie# confusion-clinic# curiosity_lab# deal-insights-dev# engineering# frontend# general# intra-changes#jiminny-bg• people-with-copilo.• people-with-zoom-….# platform-team# platform-tickets# product launches# random# releases# sofia-office# support# thank-yous#t the neonle of iimi.. Direct messagesA Stefka Stovanovaa Adelina Detrovala Vasil VasilevStoyan Tomov% Galva Dimitrova3 Aneliva Angelova, ...Nikolay VankoyCodeLaravelKeractorlools# engineering9 24Messagest Canvase FilesMoreAdelina Petromey team. below is the recording from today sUserpilot intro session if vou'd like to review it.t vou nave anv questons, lust let me [URL_WITH_CREDENTIALS] HS_local [jiminny@localhost]A console [PROD]c) DatabaseActivities.ong© DealsRepository.pnpf console leuy•.gitignoree audio.wav= custom.logE hubspot-journal-poll.logE laravel.log< phpunit.xml.IS ttt.js= oauth-private.kev= oauth-public.key= storage= supervisord.pidwtext-relav..sontests→ Feature> M Intearation› Servicesv NUnit> MActions> M Comnonent> M Confidurationv Mconsolev D Commands> D ActivitiesD Crm› D ElasticsearchD Reports©) AutomatedReportsCommand© ImportUsersFromCsvFileTest.ph> C Contracts> C Domain>ODTO> @ Enums> C Events> 0 Exceptions→tixtures> M Guards› → Heloers,0 Http> Intearations>D Interactionsv Mhlohs> Activity• M AiAutomationi• M Audiov MAutomatedRenorts© CreateResultsTest.php© RequestGenerateAskJiminnyRer© RequestGenerateReportJobTes© SendReportJobTest.php© SendReportMailJobTest.phpi CondDonortNotConoratodMail.le• Menlandar© SendReportNotGeneratedMailJobTest.php(c) RenortNotGeneratedlest.onpA console [STAGING]©) AutomatedReportsCommandTest.php© AskJiminnyReportActivityServiceTest.php© AutomatedReportsServiceTest.php xC) AutomatedReportsService.phgC) [EMAIL]) RequestGenerateAskJiminnyReportJobTest.phpC) RequestGenerateAsk.JiminnvReport.Job.ohd(C) SendReportNotGeneratedMail.Job.ohv@ SendReportMailJob.ohnendReportMailJobTest.ohr$42C) A'SummarvCreatedl.istener.ohv© TranscriptionAiSummaryReadyEvent.php(C) AutomatedRenortGenerated.nhn(C) EventServiceProvider.onpcreatenudgecreatedevent.ongcreateactivilyLoggedcvent.onp(C) TrackProviderInstalledEvent.phgphp apl.v2.php©) AutomatedReportResult.php<?oho110 192 V69 лv 52declare strict tvness'):namesnace Tests Unit Services Klosk AutomatedRenorts:use ...45 %class_AutomatedRenortsServiceTest extends Testcase80 usagesprivate AutomatedReportsService $service:6410 61)protected function setUp: voidf...;en 6r sprotected function tearDown: void{...}22 usagesprivate function getServicesmockuserkepos1cory = null,$mockStageRepository = nullSmockleamRepos1tory = null.): AutomatedReportsService {...}112 M#DataProvider'tr.nsformlednalvoesDataProvaden')1inublic function testTransformMediaTvoes(arrav SmediaTvnes. Arannublic function testGetMediatvoesieldDataWithoutRenortoved 7spublic function testGetMediaTvneFieldDateWithRenontCe voide+ 2025-01-01 00.00.00Inone canclect pnor ect rests unle serveces krosk Automacedkeponus Auconaredkedorsserv.cekeporcbeneraconlest.ono:20s5) Tests Unit\Services Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequencyFailed asserting that two strings are equal,-== Expectedee ee- '2022-11-15 00:00:00+'2022-10-01 00:00:001home/circleci/oroiect/tests/Unit/Services/Kiosk/AutomatedRenorts/AutomatedRenortsServiceReportGenerationTest.oho:2366) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayLoadWithWeekLyFrequencyUsesFullDaysFailed asserting that two strings are equal.--- Eynected+++ Actual- ' 2026-01-13T00:00:00+00:00'щ12024-01-11т00-00-90400-001/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:7787) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDaysFailed asserting that two strings are egual.--- Expected+++ Actual-2025-12-20T00:00:00+00:00'+^2025-12-01700:00:00+00:00'/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:8168) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayLoadWithQuarterlyFrequencyUsesFullDaysFailed asserting that two strings are equal.--- Expected*** Actuatiee ee-12025-10-20100•00-00÷90•001+12025-10-01т00:00:00+00÷00'home/cinclecinaosioct/tostc/Anit/Soaviicos/KinslAntomatadPonostc/AutomatadPonantsSonvicoRonontfanenat.innTost.nhn.85/CATIUDESTests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.1 usageThe Hunspell plugin has been deprecated. If you're not writing in Hungarian, you can safely uninstall Hunspell without affecting the dictionaries for other languages. (3 minutes agoW Windsurf Teams 79:134 UTF-8 P 4 spaces...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75390
|
|
75391
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
10
92
69
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Support\Carbon as IlluminateCarbon;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Component\AskAnything\Dtos\AskAnythingPromptDto;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Repositories\UserRepository;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Illuminate\Support\Collection;
use Jiminny\Models\AskAnything\AskAnythingPrompt;
use Jiminny\Models\AskAnything\AskAnythingPromptTarget;
use Jiminny\Models\Activity\Search;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Group;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AskAnythingRepository;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Mockery;
use PHPUnit\Framework\Attributes\DataProvider;
use Tests\TestCase;
class AutomatedReportsServiceTest extends TestCase
{
private AutomatedReportsService $service;
protected function setUp(): void
{
parent::setUp();
// Create a real instance of the service without calling the constructor
$reflection = new \ReflectionClass(AutomatedReportsService::class);
$this->service = $reflection->newInstanceWithoutConstructor();
// Manually set the dependencies using reflection
$dependencies = [
'teamRepository' => TeamRepository::class,
'groupRepository' => GroupRepository::class,
'userRepository' => UserRepository::class,
'stageRepository' => StageRepository::class,
'dealStagesService' => DealStagesService::class,
'recipientsService' => RecipientsService::class,
'automatedReportsRepository' => AutomatedReportsRepository::class,
'webhookService' => Webhook::class,
'dispatcher' => Dispatcher::class,
'activityTypeService' => ActivityTypeService::class,
'playbookCategoryRepository' => PlaybookCategoryRepository::class,
'askAnythingPromptService' => AskAnythingPromptService::class,
'activitySearchRepository' => SearchRepository::class,
'askAnythingRepository' => AskAnythingRepository::class,
];
foreach ($dependencies as $propertyName => $class) {
$property = $reflection->getProperty($propertyName);
$property->setAccessible(true);
$property->setValue($this->service, $this->createMock($class));
}
}
protected function tearDown(): void
{
parent::tearDown();
Mockery::close();
}
private function getService(
$mockUserRepository = null,
$mockStageRepository = null,
$mockTeamRepository = null,
): AutomatedReportsService {
return new AutomatedReportsService(
($mockTeamRepository ?? $this->createMock(TeamRepository::class)),
$this->createMock(GroupRepository::class),
($mockUserRepository ?? $this->createMock(UserRepository::class)),
($mockStageRepository ?? $this->createMock(StageRepository::class)),
$this->createMock(DealStagesService::class),
$this->createMock(RecipientsService::class),
$this->createMock(AutomatedReportsRepository::class),
$this->createMock(Webhook::class),
$this->createMock(Dispatcher::class),
$this->createMock(ActivityTypeService::class),
$this->createMock(PlaybookCategoryRepository::class),
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
#[DataProvider('transformMediaTypesDataProvider')]
public function testTransformMediaTypes(array $mediaTypes, array $expected): void
{
$report = new AutomatedReport(['media_types' => $mediaTypes]);
$reflection = new \ReflectionClass(AutomatedReportsService::class);
$method = $reflection->getMethod('transformMediaTypes');
$result = $method->invoke($this->service, $report);
$this->assertEquals($expected, $result);
}
public function testGetMediaTypeFieldDataWithoutReport(): void
{
$result = $this->service->getMediaTypeFieldData(null);
$this->assertIsArray($result);
$this->assertArrayHasKey('value', $result);
$this->assertEmpty($result['value']);
$this->assertEquals('media_types', $result['id']);
}
public function testGetMediaTypeFieldDataWithReport(): void
{
$mediaTypes = ['pdf', 'podcast'];
$report = new AutomatedReport(['media_types' => $mediaTypes]);
$result = $this->service->getMediaTypeFieldData($report);
$expectedValue = [
['id' => 'pdf', 'name' => 'PDF'],
['id' => 'podcast', 'name' => 'Podcast'],
];
$this->assertIsArray($result);
$this->assertArrayHasKey('value', $result);
$this->assertEquals($expectedValue, $result['value']);
}
public static function transformMediaTypesDataProvider(): array
{
return [
'empty array' => [
'mediaTypes' => [],
'expected' => [],
],
'pdf only' => [
'mediaTypes' => ['pdf'],
'expected' => [
['id' => 'pdf', 'name' => 'PDF'],
],
],
'podcast only' => [
'mediaTypes' => ['podcast'],
'expected' => [
['id' => 'podcast', 'name' => 'Podcast'],
],
],
'both pdf and podcast' => [
'mediaTypes' => ['pdf', 'podcast'],
'expected' => [
['id' => 'pdf', 'name' => 'PDF'],
['id' => 'podcast', 'name' => 'Podcast'],
],
],
'with invalid type' => [
'mediaTypes' => ['pdf', 'invalid', 'podcast'],
'expected' => [
['id' => 'pdf', 'name' => 'PDF'],
['id' => 'podcast', 'name' => 'Podcast'],
],
],
];
}
#[DataProvider('hasCallTypeConferenceDataProvider')]
public function testHasCallTypeConference(array $callTypes, bool $expected): void
{
$report = $this->createMock(AutomatedReport::class);
$report->method('getCallTypes')->willReturn($callTypes);
$result = $this->service->hasCallTypeConference($report);
$this->assertEquals($expected, $result);
}
#[DataProvider('hasCallTypeDialerDataProvider')]
public function testHasCallTypeDialer(array $callTypes, bool $expected): void
{
$report = $this->createMock(AutomatedReport::class);
$report->method('getCallTypes')->willReturn($callTypes);
$result = $this->service->hasCallTypeDialer($report);
$this->assertEquals($expected, $result);
}
public static function hasCallTypeConferenceDataProvider(): array
{
return [
'has conference' => [
'callTypes' => ['conference', 'dialer'],
'expected' => true,
],
'does not have conference' => [
'callTypes' => ['dialer', 'other'],
'expected' => false,
],
'empty call types' => [
'callTypes' => [],
'expected' => false,
],
];
}
public static function hasCallTypeDialerDataProvider(): array
{
return [
'has dialer' => [
'callTypes' => ['conference', 'dialer'],
'expected' => true,
],
'does not have dialer' => [
'callTypes' => ['conference', 'other'],
'expected' => false,
],
'empty call types' => [
'callTypes' => [],
'expected' => false,
],
];
}
public function testTransformReportResultsWithEmptyCollection(): void
{
$emptyCollection = new Collection([]);
$result = $this->service->transformReportResults($emptyCollection);
$this->assertIsArray($result);
$this->assertEmpty($result);
}
public function testTransformReportResultsStructure(): void
{
// Create a mock AutomatedReportResult with minimal setup to test structure
$mockReportResult = $this->createMockReportResult();
$collection = new Collection([$mockReportResult]);
$result = $this->service->transformReportResults($collection);
$this->assertIsArray($result);
$this->assertCount(1, $result);
$transformedResult = $result[0];
// Verify all expected keys are present
$expectedKeys = [
'id', 'name', 'frequency', 'recipients',
'report_type', 'media_type', 'downloadUrl', 'viewUrl', 'generated_at',
];
foreach ($expectedKeys as $key) {
$this->assertArrayHasKey($key, $transformedResult);
}
// Verify structure of nested arrays
$this->assertIsArray($transformedResult['frequency']);
$this->assertArrayHasKey('id', $transformedResult['frequency']);
$this->assertArrayHasKey('name', $transformedResult['frequency']);
$this->assertIsArray($transformedResult['report_type']);
$this->assertArrayHasKey('id', $transformedResult['report_type']);
$this->assertArrayHasKey('name', $transformedResult['report_type']);
$this->assertIsArray($transformedResult['recipients']);
// Verify TODO fields are null as expected
$this->assertEquals(AutomatedReportsService::MEDIA_TYPE_PODCAST, $transformedResult['media_type']);
$this->assertEquals(route('ai-reports.audio.download', ['uuid' => 'test-uuid']), $transformedResult['downloadUrl']);
$this->assertEquals(route('ai-reports.audio.view', ['uuid' => 'test-uuid']), $transformedResult['viewUrl']);
}
public function testTransformReportResultsWithMultipleResults(): void
{
$mockReportResult1 = $this->createMockReportResult('result-uuid-1', 'exec_summary');
$mockReportResult2 = $this->createMockReportResult('result-uuid-2', 'coaching_profiles');
$collection = new Collection([$mockReportResult1, $mockReportResult2]);
$result = $this->service->transformReportResults($collection);
$this->assertIsArray($result);
$this->assertCount(2, $result);
// Verify different UUIDs
$this->assertEquals('result-uuid-1', $result[0]['id']);
$this->assertEquals('result-uuid-2', $result[1]['id']);
// Verify both results have the expected structure
foreach ($result as $transformedResult) {
$this->assertArrayHasKey('id', $transformedResult);
$this->assertArrayHasKey('name', $transformedResult);
$this->assertArrayHasKey('frequency', $transformedResult);
$this->assertArrayHasKey('recipients', $transformedResult);
$this->assertArrayHasKey('report_type', $transformedResult);
}
}
#[DataProvider('isUserRecipientOfReportDataProvider')]
public function testIsUserRecipientOfReport(int $userId, array $recipients, bool $expected): void
{
// Create mock User
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn($userId);
$mockUser->method('getGroupId')->willReturn(null);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn($recipients);
$mockReport->method('isAskJiminnyReport')->willReturn(false);
$mockReport->method('getGroups')->willReturn([]);
$result = $this->service->isUserRecipientOfReport($mockUser, $mockReport);
$this->assertEquals($expected, $result);
}
#[DataProvider('isUserRecipientOfAskJiminnyReportDataProvider')]
public function testIsUserRecipientOfAskJiminnyReportViaGroup(
int $userId,
?int $groupId,
array $recipients,
array $reportGroups,
bool $expected,
): void {
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn($userId);
$mockUser->method('getGroupId')->willReturn($groupId);
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn($recipients);
$mockReport->method('isAskJiminnyReport')->willReturn(true);
$mockReport->method('getGroups')->willReturn($reportGroups);
$this->assertSame($expected, $this->service->isUserRecipientOfReport($mockUser, $mockReport));
}
public function testIsUserRecipientOfNonAskJiminnyReportIgnoresGroups(): void
{
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn(123);
$mockUser->method('getGroupId')->willReturn(5);
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['users' => []]);
$mockReport->method('isAskJiminnyReport')->willReturn(false);
$mockReport->method('getGroups')->willReturn([5]);
$this->assertFalse($this->service->isUserRecipientOfReport($mockUser, $mockReport));
}
public static function isUserRecipientOfAskJiminnyReportDataProvider(): array
{
return [
'group member - ask jiminny' => [
'userId' => 123,
'groupId' => 7,
'recipients' => ['users' => []],
'reportGroups' => [7],
'expected' => true,
],
'group mismatch - ask jiminny' => [
'userId' => 123,
'groupId' => 9,
'recipients' => ['users' => []],
'reportGroups' => [7, 8],
'expected' => false,
],
'user with no group - ask jiminny' => [
'userId' => 123,
'groupId' => null,
'recipients' => ['users' => []],
'reportGroups' => [7],
'expected' => false,
],
'recipient users take precedence over group' => [
'userId' => 123,
'groupId' => null,
'recipients' => ['users' => [123]],
'reportGroups' => [],
'expected' => true,
],
];
}
public function testIsUserRecipientOfReportWithEmptyRecipients(): void
{
// Create mock User
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn(123);
// Create mock AutomatedReport with no recipients
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn([]);
$result = $this->service->isUserRecipientOfReport($mockUser, $mockReport);
$this->assertFalse($result);
}
public function testIsUserRecipientOfReportWithNoUsersKey(): void
{
// Create mock User
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn(123);
// Create mock AutomatedReport with recipients but no 'users' key
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['other_key' => [456, 789]]);
$result = $this->service->isUserRecipientOfReport($mockUser, $mockReport);
$this->assertFalse($result);
}
public static function isUserRecipientOfReportDataProvider(): array
{
return [
'user is recipient - single user' => [
'userId' => 123,
'recipients' => ['users' => [123]],
'expected' => true,
],
'user is recipient - multiple users' => [
'userId' => 456,
'recipients' => ['users' => [123, 456, 789]],
'expected' => true,
],
'user is not recipient - single user' => [
'userId' => 999,
'recipients' => ['users' => [123]],
'expected' => false,
],
'user is not recipient - multiple users' => [
'userId' => 999,
'recipients' => ['users' => [123, 456, 789]],
'expected' => false,
],
'user is recipient - string IDs converted to int' => [
'userId' => 123,
'recipients' => ['users' => ['123', '456']],
'expected' => true,
],
'user is not recipient - string IDs converted to int' => [
'userId' => 999,
'recipients' => ['users' => ['123', '456']],
'expected' => false,
],
'empty users array' => [
'userId' => 123,
'recipients' => ['users' => []],
'expected' => false,
],
];
}
private function createMockReportResult(string $uuid = 'test-uuid', string $reportType = 'exec_summary'): AutomatedReportResult
{
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getFrequency')->willReturn('weekly');
$mockReport->method('getRecipients')->willReturn(['users' => [1, 2]]);
$mockReport->method('getGroups')->willReturn([10, 20]);
$mockReport->method('getType')->willReturn($reportType);
// Create mock Team
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
// Create mock Group
$mockGroup = $this->createMock(\Jiminny\Models\Group::class);
$mockGroup->method('getUuid')->willReturn('group-uuid-10');
$mockGroup->method('getName')->willReturn('Test Team');
$mockQueryBuilder = Mockery::mock();
$mockQueryBuilder->shouldReceive('where')->andReturnSelf();
$mockQueryBuilder->shouldReceive('first')->andReturn($mockGroup);
$dataRelation = Mockery::mock(HasMany::class);
$dataRelation->shouldReceive('where')->andReturn($mockQueryBuilder);
$dataRelation->shouldReceive('get')->andReturn(
new \Illuminate\Database\Eloquent\Collection([$mockGroup])
);
$mockTeam->method('groups')->willReturn($dataRelation);
$mockReport->method('getTeam')->willReturn($mockTeam);
// Create mock AutomatedReportResult
$mockReportResult = $this->createMock(AutomatedReportResult::class);
$mockReportResult->method('getUuid')->willReturn($uuid);
$mockReportResult->method('getGeneratedAt')->willReturn(
\Illuminate\Support\Carbon::parse('2024-01-15T10:30:00Z')
);
$mockReportResult->method('getReport')->willReturn($mockReport);
// Mock methods used in getReportFileName
$mockReportResult->method('getReportType')->willReturn($reportType);
$mockReportResult->method('getFromDate')->willReturn(
\Illuminate\Support\Carbon::parse('2024-01-08')
);
$mockReportResult->method('getToDate')->willReturn(
\Illuminate\Support\Carbon::parse('2024-01-15')
);
$mockReportResult->method('getGroups')->willReturn([10]);
$mockReportResult->method('getMediaType')->willReturn(AutomatedReportsService::MEDIA_TYPE_PODCAST);
return $mockReportResult;
}
#[DataProvider('getUsersUuidsDataProvider')]
public function testGetUsersUuids(array $recipients, array $mockUsers, array $expectedUuids): void
{
// Create mock UserRepository
$mockUserRepository = $this->createMock(UserRepository::class);
// Configure the mock to return specific users for specific IDs using a callback
$mockUserRepository->method('find')
->willReturnCallback(function ($userId) use ($mockUsers) {
if (! isset($mockUsers[$userId])) {
return null;
}
$userUuid = $mockUsers[$userId]['uuid'] ?? null;
if ($userUuid === null) {
return null;
}
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getUuid')->willReturn((string) $userUuid);
return $mockUser;
});
// Create service with mocked UserRepository
$automatedReportsService = $this->getService(mockUserRepository: $mockUserRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn($recipients);
$result = $automatedReportsService->getUsersUuids($mockReport);
$this->assertEquals($expectedUuids, $result);
}
public function testGetUsersUuidsWithEmptyRecipients(): void
{
// Create mock AutomatedReport with empty recipients
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn([]);
$result = $this->service->getUsersUuids($mockReport);
$this->assertEquals([], $result);
}
public function testGetUsersUuidsWithNoUsersKey(): void
{
// Create mock AutomatedReport with recipients but no 'users' key
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['other_key' => [1, 2, 3]]);
$result = $this->service->getUsersUuids($mockReport);
$this->assertEquals([], $result);
}
public function testGetUsersUuidsWithNonExistentUsers(): void
{
// Create mock UserRepository that returns null for all users
$mockUserRepository = $this->createMock(UserRepository::class);
$mockUserRepository->method('find')->willReturn(null);
// Create service with mocked UserRepository
$automatedReportsService = $this->getService(mockUserRepository: $mockUserRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['users' => [1, 2, 3]]);
$result = $automatedReportsService->getUsersUuids($mockReport);
// Should return array with null values for non-existent users
$this->assertEquals([], $result);
}
public static function getUsersUuidsDataProvider(): array
{
return [
'single user found' => [
'recipients' => ['users' => [123]],
'mockUsers' => [
123 => ['id' => 123, 'uuid' => 'user-uuid-123'],
],
'expectedUuids' => ['user-uuid-123'],
],
'multiple users found' => [
'recipients' => ['users' => [123, 456, 789]],
'mockUsers' => [
123 => ['id' => 123, 'uuid' => 'user-uuid-123'],
456 => ['id' => 456, 'uuid' => 'user-uuid-456'],
789 => ['id' => 789, 'uuid' => 'user-uuid-789'],
],
'expectedUuids' => ['user-uuid-123', 'user-uuid-456', 'user-uuid-789'],
],
'mixed found and not found users' => [
'recipients' => ['users' => [123, 456, 789]],
'mockUsers' => [
123 => ['id' => 123, 'uuid' => 'user-uuid-123'],
// 456 not found in DB
789 => ['id' => 789, 'uuid' => 'user-uuid-789'],
],
'expectedUuids' => ['user-uuid-123', 'user-uuid-789'], // Updated to reflect that nulls are filtered out
],
'empty users array' => [
'recipients' => ['users' => []],
'mockUsers' => [],
'expectedUuids' => [],
],
'all users not found' => [
'recipients' => ['users' => [123, 456]],
'mockUsers' => [], // No users found
'expectedUuids' => [], // Updated to reflect that nulls are filtered out
],
];
}
#[DataProvider('getCurrentDealStagesUuidsDataProvider')]
public function testGetCurrentDealStagesUuids(array $currentDealStages, array $mockStages, array $expectedUuids): void
{
// Create mock StageRepository
$mockStageRepository = $this->createMock(StageRepository::class);
// Configure the mock to return specific stages for specific IDs using a callback
$mockStageRepository->method('find')
->willReturnCallback(function ($stageId) use ($mockStages) {
if (! isset($mockStages[$stageId])) {
return null;
}
$stageUuid = $mockStages[$stageId]['uuid'] ?? null;
if ($stageUuid === null) {
return null;
}
$mockStage = $this->createMock(\Jiminny\Models\Stage::class);
$mockStage->method('getUuid')->willReturn((string) $stageUuid);
return $mockStage;
});
// Create service with mocked StageRepository
$automatedReportsService = $this->getService(mockStageRepository: $mockStageRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getCurrentDealStages')->willReturn($currentDealStages);
$result = $automatedReportsService->getCurrentDealStagesUuids($mockReport);
$this->assertEquals($expectedUuids, $result);
}
public function testGetCurrentDealStagesUuidsWithEmptyStages(): void
{
// Create mock AutomatedReport with empty current deal stages
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getCurrentDealStages')->willReturn([]);
$result = $this->service->getCurrentDealStagesUuids($mockReport);
$this->assertEquals([], $result);
}
public function testGetCurrentDealStagesUuidsWithNonExistentStages(): void
{
// Create mock StageRepository that returns null for all stages
$mockStageRepository = $this->createMock(StageRepository::class);
$mockStageRepository->method('find')->willReturn(null);
// Create service with mocked StageRepository
$automatedReportsService = $this->getService(mockStageRepository: $mockStageRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getCurrentDealStages')->willReturn([1, 2, 3]);
$result = $automatedReportsService->getCurrentDealStagesUuids($mockReport);
// Should return array with null values for non-existent stages
$this->assertEquals([], $result);
}
public static function getCurrentDealStagesUuidsDataProvider(): array
{
return [
'single stage found' => [
'currentDealStages' => [10],
'mockStages' => [
10 => ['id' => 10, 'uuid' => 'stage-uuid-10'],
],
'expectedUuids' => ['stage-uuid-10'],
],
'multiple stages found' => [
'currentDealStages' => [10, 20, 30],
'mockStages' => [
10 => ['id' => 10, 'uuid' => 'stage-uuid-10'],
20 => ['id' => 20, 'uuid' => 'stage-uuid-20'],
30 => ['id' => 30, 'uuid' => 'stage-uuid-30'],
],
'expectedUuids' => ['stage-uuid-10', 'stage-uuid-20', 'stage-uuid-30'],
],
'mixed found and not found stages' => [
'currentDealStages' => [10, 20, 30],
'mockStages' => [
10 => ['id' => 10, 'uuid' => 'stage-uuid-10'],
// 20 not found in DB
30 => ['id' => 30, 'uuid' => 'stage-uuid-30'],
],
'expectedUuids' => ['stage-uuid-10', 'stage-uuid-30'], // Updated to reflect that nulls are filtered out
],
'empty stages array' => [
'currentDealStages' => [],
'mockStages' => [],
'expectedUuids' => [],
],
'all stages not found' => [
'currentDealStages' => [10, 20],
'mockStages' => [], // No stages found
'expectedUuids' => [], // Updated to reflect that nulls are filtered out
],
];
}
#[DataProvider('getTeamGroupsDataProvider')]
public function testGetTeamGroups(string $teamUuid, ?array $mockTeamData, array $mockGroups, array $expectedResult): void
{
// Create mock TeamRepository
$mockTeamRepository = $this->createMock(TeamRepository::class);
if ($mockTeamData === null) {
// Team not found
$mockTeamRepository->method('idOrUuid')
->with($teamUuid)
->willReturn(null);
} else {
// Team found - create mock team with groups
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
// Create mock groups collection
$mockGroupsCollection = $this->createMock(\Illuminate\Database\Eloquent\Collection::class);
// Create mock Group objects
$groupObjects = [];
foreach ($mockGroups as $groupData) {
$mockGroup = $this->createMock(\Jiminny\Models\Group::class);
$mockGroup->method('getUuid')->willReturn($groupData['id']);
$mockGroup->method('getName')->willReturn($groupData['name']);
$groupObjects[] = $mockGroup;
}
// Mock the groups collection to return our mock groups
$mockGroupsCollection->method('getIterator')->willReturn(new \ArrayIterator($groupObjects));
// Mock the groups() relation
$mockGroupsRelation = $this->createMock(\Illuminate\Database\Eloquent\Relations\HasMany::class);
$mockGroupsRelation->method('get')->willReturn($mockGroupsCollection);
$mockTeam->method('groups')->willReturn($mockGroupsRelation);
$mockTeamRepository->method('idOrUuid')
->with($teamUuid)
->willReturn($mockTeam);
}
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeamGroups($teamUuid);
$this->assertEquals($expectedResult, $result);
}
public function testGetTeamGroupsWithNonExistentTeam(): void
{
// Create mock TeamRepository that returns null (team not found)
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('idOrUuid')->willReturn(null);
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeamGroups('non-existent-team-uuid');
$this->assertEquals([], $result);
}
public function testGetTeamGroupsWithEmptyGroups(): void
{
// Create mock team with no groups
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
// Create empty groups collection
$mockGroupsCollection = $this->createMock(\Illuminate\Database\Eloquent\Collection::class);
$mockGroupsCollection->method('getIterator')->willReturn(new \ArrayIterator([]));
$mockGroupsRelation = $this->createMock(\Illuminate\Database\Eloquent\Relations\HasMany::class);
$mockGroupsRelation->method('get')->willReturn($mockGroupsCollection);
$mockTeam->method('groups')->willReturn($mockGroupsRelation);
// Create mock TeamRepository
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('idOrUuid')->willReturn($mockTeam);
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeamGroups('team-with-no-groups');
$this->assertEquals([], $result);
}
public static function getTeamGroupsDataProvider(): array
{
return [
'team with single group' => [
'teamUuid' => 'team-uuid-123',
'mockTeamData' => ['id' => 'team-uuid-123', 'name' => 'Test Team'],
'mockGroups' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
],
'expectedResult' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
],
],
'team with multiple groups' => [
'teamUuid' => 'team-uuid-456',
'mockTeamData' => ['id' => 'team-uuid-456', 'name' => 'Another Team'],
'mockGroups' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
['id' => 'group-uuid-2', 'name' => 'Marketing Team'],
['id' => 'group-uuid-3', 'name' => 'Support Team'],
],
'expectedResult' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
['id' => 'group-uuid-2', 'name' => 'Marketing Team'],
['id' => 'group-uuid-3', 'name' => 'Support Team'],
],
],
'team not found' => [
'teamUuid' => 'non-existent-uuid',
'mockTeamData' => null,
'mockGroups' => [],
'expectedResult' => [],
],
'team with no groups' => [
'teamUuid' => 'team-uuid-empty',
'mockTeamData' => ['id' => 'team-uuid-empty', 'name' => 'Empty Team'],
'mockGroups' => [],
'expectedResult' => [],
],
];
}
#[DataProvider('getTeamsDataProvider')]
public function testGetTeams(array $mockTeams, array $expectedResult): void
{
// Create mock TeamRepository
$mockTeamRepository = $this->createMock(TeamRepository::class);
// Create mock Team objects
$teamObjects = [];
foreach ($mockTeams as $teamData) {
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
$mockTeam->method('getUuid')->willReturn($teamData['id']);
$mockTeam->method('getName')->willReturn($teamData['name']);
$mockTeam->method('hasFeature')
->with(\Jiminny\Models\Feature\FeatureEnum::AUTOMATED_REPORTS)
->willReturn($teamData['hasAutomatedReports']);
$teamObjects[] = $mockTeam;
}
// Mock the repository to return a Collection (not array)
$mockTeamRepository->method('getTeamsForKiosk')
->with('active')
->willReturn(new Collection($teamObjects));
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeams();
$this->assertEquals($expectedResult, $result);
}
public function testGetTeamsWithNoTeams(): void
{
// Create mock TeamRepository that returns empty Collection
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('getTeamsForKiosk')->willReturn(new Collection([]));
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeams();
$this->assertEquals([], $result);
}
public function testGetTeamsWithAllTeamsWithoutFeature(): void
{
// Create mock teams without AUTOMATED_REPORTS feature
$mockTeam1 = $this->createMock(\Jiminny\Models\Team::class);
$mockTeam1->method('hasFeature')
->with(\Jiminny\Models\Feature\FeatureEnum::AUTOMATED_REPORTS)
->willReturn(false);
$mockTeam2 = $this->createMock(\Jiminny\Models\Team::class);
$mockTeam2->method('hasFeature')
->with(\Jiminny\Models\Feature\FeatureEnum::AUTOMATED_REPORTS)
->willReturn(false);
// Create mock TeamRepository that returns Collection
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('getTeamsForKiosk')->willReturn(new Collection([$mockTeam1, $mockTeam2]));
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeams();
$this->assertEquals([], $result);
}
public static function getTeamsDataProvider(): array
{
return [
'single team with feature' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => true,
],
],
'expectedResult' => [
['id' => 'team-uuid-1', 'name' => 'Sales Team'],
],
],
'multiple teams with feature' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => true,
],
[
'id' => 'team-uuid-2',
'name' => 'Marketing Team',
'hasAutomatedReports' => true,
],
[
'id' => 'team-uuid-3',
'name' => 'Support Team',
'hasAutomatedReports' => true,
],
],
'expectedResult' => [
['id' => 'team-uuid-1', 'name' => 'Sales Team'],
['id' => 'team-uuid-2', 'name' => 'Marketing Team'],
['id' => 'team-uuid-3', 'name' => 'Support Team'],
],
],
'mixed teams - some with feature, some without' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => true,
],
[
'id' => 'team-uuid-2',
'name' => 'Marketing Team',
'hasAutomatedReports' => false,
],
[
'id' => 'team-uuid-3',
'name' => 'Support Team',
'hasAutomatedReports' => true,
],
],
'expectedResult' => [
['id' => 'team-uuid-1', 'name' => 'Sales Team'],
['id' => 'team-uuid-3', 'name' => 'Support Team'],
],
],
'all teams without feature' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => false,
],
[
'id' => 'team-uuid-2',
'name' => 'Marketing Team',
'hasAutomatedReports' => false,
],
],
'expectedResult' => [],
],
'empty teams array' => [
'mockTeams' => [],
'expectedResult' => [],
],
];
}
#[DataProvider('deleteS3FilesDataProvider')]
public function testDeleteS3Files(
string $mediaType,
array $expectedFileExtensions,
array $existingFiles,
string $pathSuffix,
int $expectedDeletes
): void {
// Arrange
$teamUuid = 'team-uuid-123';
$reportUuid = 'report-uuid-456';
$basePath = sprintf('%s/reports/%s', $teamUuid, $reportUuid);
$team = Mockery::mock(Team::class);
$team->allows('getUuid')->andReturn($teamUuid);
$report = Mockery::mock(AutomatedReport::class);
$report->allows('getTeam')->andReturn($team);
$result = Mockery::mock(AutomatedReportResult::class);
$result->allows('getReport')->andReturn($report);
$result->allows('getUuid')->andReturn($reportUuid);
$result->allows('getMediaType')->andReturn($mediaType);
Storage::fake();
Log::shouldReceive('info')->times($expectedDeletes);
foreach ($existingFiles as $extension) {
$filePath = $basePath . $pathSuffix . '.' . $extension;
Storage::put($filePath, 'dummy content');
}
// Act
$this->service->deleteS3Files($result);
// Assert
foreach ($expectedFileExtensions as $extension) {
$filePath = $basePath . $pathSuffix . '.' . $extension;
if (in_array($extension, $existingFiles, true)) {
Storage::assertMissing($filePath);
} else {
// To be sure no unexpected files were created and deleted
Storage::assertMissing($filePath);
}
}
}
public static function deleteS3FilesDataProvider(): array
{
return [
'PDF report, all files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PDF,
'expectedFileExtensions' => ['html', 'MD', 'pdf'],
'existingFiles' => ['html', 'MD', 'pdf'],
'pathSuffix' => '',
'expectedDeletes' => 3,
],
'PDF report, some files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PDF,
'expectedFileExtensions' => ['html', 'MD', 'pdf'],
'existingFiles' => ['html', 'pdf'],
'pathSuffix' => '',
'expectedDeletes' => 2,
],
'PDF report, no files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PDF,
'expectedFileExtensions' => ['html', 'MD', 'pdf'],
'existingFiles' => [],
'pathSuffix' => '',
'expectedDeletes' => 0,
],
'Podcast report, all files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'expectedFileExtensions' => ['json', 'mp3', 'ssml'],
'existingFiles' => ['json', 'mp3', 'ssml'],
'pathSuffix' => '_podcast',
'expectedDeletes' => 3,
],
'Podcast report, some files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'expectedFileExtensions' => ['json', 'mp3', 'ssml'],
'existingFiles' => ['mp3'],
'pathSuffix' => '_podcast',
'expectedDeletes' => 1,
],
'Podcast report, no files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'expectedFileExtensions' => ['json', 'mp3', 'ssml'],
'existingFiles' => [],
'pathSuffix' => '_podcast',
'expectedDeletes' => 0,
],
'Other media type, should do nothing' => [
'mediaType' => 'some_other_type',
'expectedFileExtensions' => [],
'existingFiles' => [],
'pathSuffix' => '',
'expectedDeletes' => 0,
],
];
}
public function testDeleteReportsResultsInRetentionPeriodWithLogging(): void
{
// Create mocks for the test
$automatedReportsService = Mockery::mock(AutomatedReportsService::class);
$team = Mockery::mock(Team::class);
$team->shouldReceive('getId')->andReturn(123);
$from = now()->subDays(30);
$to = now();
$source = 'test-source';
// Expect the method to be called with specific parameters
$automatedReportsService->shouldReceive('deleteReportsResultsInRetentionPeriodWithLogging')
->once()
->with(
$team,
Mockery::on(function ($arg) use ($from) {
return $arg->timestamp === $from->timestamp;
}),
Mockery::on(function ($arg) use ($to) {
return $arg->timestamp === $to->timestamp;
}),
$source
)
->andReturn(5);
// Call the method and verify the result
$result = $automatedReportsService->deleteReportsResultsInRetentionPeriodWithLogging(
$team,
$from,
$to,
$source
);
$this->assertEquals(5, $result);
}
#[DataProvider('sanitizeFileNameDataProvider')]
public function testSanitizeFileName(string $input, string $expected): void
{
$result = $this->service->sanitizeFileName($input);
$this->assertEquals($expected, $result);
}
public static function sanitizeFileNameDataProvider(): array
{
return [
'no special characters' => [
'input' => 'Exec Summary - Sep 2025 - Business Development Team',
'expected' => 'Exec Summary - Sep 2025 - Business Development Team',
],
'forward slash in team name' => [
'input' => 'Exec Summary - Sep 2025 - ND/IRV',
'expected' => 'Exec Summary - Sep 2025 - ND-IRV',
],
'backslash in team name' => [
'input' => 'Exec Summary - Sep 2025 - ND\IRV',
'expected' => 'Exec Summary - Sep 2025 - ND-IRV',
],
'multiple forward slashes' => [
'input' => 'Report - Team A/B/C',
'expected' => 'Report - Team A-B-C',
],
'multiple backslashes' => [
'input' => 'Report - Team A\B\C',
'expected' => 'Report - Team A-B-C',
],
'mixed slashes and backslashes' => [
'input' => 'Report - Team A/B\C',
'expected' => 'Report - Team A-B-C',
],
'complex team name with slashes' => [
'input' => 'Exec Summary - Sep 2025 - Business Development Team - ND/IRV, Net Driven - Acquisition (Sales)',
'expected' => 'Exec Summary - Sep 2025 - Business Development Team - ND-IRV, Net Driven - Acquisition (Sales)',
],
'only slashes' => [
'input' => '//\\\\',
'expected' => '----',
],
'empty string' => [
'input' => '',
'expected' => '',
],
'slash at start' => [
'input' => '/Report Name',
'expected' => '-Report Name',
],
'slash at end' => [
'input' => 'Report Name/',
'expected' => 'Report Name-',
],
];
}
public function testGetReportFileNameSanitizesOutput(): void
{
// Create mock GroupRepository
$mockGroupRepository = $this->createMock(GroupRepository::class);
// Create mock Group with slash in name
$mockGroup = $this->createMock(\Jiminny\Models\Group::class);
$mockGroup->method('getName')->willReturn('ND/IRV, Net Driven - Acquisition (Sales)');
$mockGroupRepository->method('find')->willReturn($mockGroup);
// Create service with mocked GroupRepository
$service = new AutomatedReportsService(
$this-...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75391
|
|
75392
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use DateTimeInterface;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Group;
use Jiminny\Models\Team;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceActivitiesCountTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Group $group;
protected function setUp(): void
{
parent::setUp();
// Create a mock team
$team = $this->createMock(Team::class);
$team->method('getId')->willReturn(123);
$team->method('getUuid')->willReturn('6473c918-d8db-4ded-a52b-4febfd7b7c02');
$team->method('hasFeature')
->with(FeatureEnum::AUTOMATED_REPORTS)
->willReturn(true);
// Create a mock group
$this->group = $this->createMock(Group::class);
$this->group->method('getId')->willReturn(1);
$this->group->method('getTeamId')->willReturn(123);
// Create mocks for dependencies
$teamRepository = $this->createMock(TeamRepository::class);
$teamRepository->method('idOrUuid')
->willReturn($team);
$groupRepository = $this->createMock(GroupRepository::class);
$groupRepository->method('findByUuid')
->willReturnCallback(function ($uuid) {
if ($uuid === '0cdece0a-7b12-49ae-8b7e-366bbb6d42d2') {
return $this->group;
}
return null;
});
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create a real service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$automatedReportsRepository,
$webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
public function testGetActivitiesCountPayloadWithOneOffFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up input payload with one_off frequency and explicit date range
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'one_off',
'start_date_period' => '2025-07-20 00:00:00',
'end_date_period' => '2025-07-30 00:00:00',
'call_type' => ['conference'],
'media_types' => ['pdf'],
'min_call_duration' => 15,
'max_call_duration' => 120,
'min_deal_value' => 5000,
'max_deal_value' => 50000,
'additional_prompt_input' => 'Custom Prompt',
];
// Call the method directly
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result contains the expected fields
$this->assertArrayHasKey('team_id', $result);
$this->assertArrayHasKey('report_type', $result);
$this->assertArrayHasKey('from_date', $result);
$this->assertArrayHasKey('to_date', $result);
$this->assertArrayHasKey('call_types', $result);
$this->assertArrayHasKey('call_duration_min_seconds', $result);
$this->assertArrayHasKey('call_duration_max_seconds', $result);
$this->assertArrayHasKey('deal_min_value', $result);
$this->assertArrayHasKey('deal_max_value', $result);
$this->assertArrayHasKey('special_requirements', $result);
// Assert specific values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals(['conference'], $result['call_types']);
$this->assertEquals(15 * 60, $result['call_duration_min_seconds']);
$this->assertEquals(120 * 60, $result['call_duration_max_seconds']);
$this->assertEquals(5000, $result['deal_min_value']);
$this->assertEquals(50000, $result['deal_max_value']);
$this->assertEquals('Custom Prompt', $result['special_requirements']);
// Verify date formats (RFC3339)
$fromDate = Carbon::parse('2025-07-20 00:00:00');
$toDate = Carbon::parse('2025-07-30 00:00:00');
$this->assertEquals($fromDate->format(DateTimeInterface::RFC3339), $result['from_date']);
$this->assertEquals($toDate->format(DateTimeInterface::RFC3339), $result['to_date']);
}
public function testGetActivitiesCountPayloadWithWeeklyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with weekly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'weekly',
'call_type' => ['conference', 'dialer'],
'media_types' => ['pdf', 'podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Get the expected date range for weekly frequency
// Weekly: subWeeks(1).startOfDay() to subDay().endOfDay()
$now = Carbon::now();
$fromDate = $now->copy()->subWeeks(1)->startOfDay();
$toDate = $now->copy()->subDay()->endOfDay();
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
$this->assertEquals(['conference', 'dialer'], $result['call_types']);
$this->assertEquals([], $result['call_deal_stage']);
$this->assertEquals([], $result['current_deal_stage']);
$this->assertNull($result['deal_min_value']);
$this->assertNull($result['deal_max_value']);
$this->assertNull($result['call_duration_min_seconds']);
$this->assertNull($result['call_duration_max_seconds']);
$this->assertNull($result['special_requirements']);
$this->assertNull($result['request_id']);
$this->assertNull($result['callback_url']);
// Check exact dates with full calendar days
$this->assertEquals('2025-08-05T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2025-08-11T23:59:59+00:00', $result['to_date']);
Carbon::setTestNow();
}
public function testGetActivitiesCountPayloadWithMonthlyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with monthly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'monthly',
'media_types' => ['podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
// Check that dates are in RFC3339 format
// Use assertStringStartsWith to avoid exact timestamp comparison issues
$this->assertStringStartsWith('2025-07', $result['from_date']);
$this->assertStringStartsWith('2025-08-11', $result['to_date']);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
There were 8 failures:
1) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75392
|
|
75413
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
testGetActivitiesCountPayloadWithWeeklyFrequency
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/1
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use DateTimeInterface;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Group;
use Jiminny\Models\Team;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceActivitiesCountTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Group $group;
protected function setUp(): void
{
parent::setUp();
// Create a mock team
$team = $this->createMock(Team::class);
$team->method('getId')->willReturn(123);
$team->method('getUuid')->willReturn('6473c918-d8db-4ded-a52b-4febfd7b7c02');
$team->method('hasFeature')
->with(FeatureEnum::AUTOMATED_REPORTS)
->willReturn(true);
// Create a mock group
$this->group = $this->createMock(Group::class);
$this->group->method('getId')->willReturn(1);
$this->group->method('getTeamId')->willReturn(123);
// Create mocks for dependencies
$teamRepository = $this->createMock(TeamRepository::class);
$teamRepository->method('idOrUuid')
->willReturn($team);
$groupRepository = $this->createMock(GroupRepository::class);
$groupRepository->method('findByUuid')
->willReturnCallback(function ($uuid) {
if ($uuid === '0cdece0a-7b12-49ae-8b7e-366bbb6d42d2') {
return $this->group;
}
return null;
});
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create a real service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$automatedReportsRepository,
$webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
public function testGetActivitiesCountPayloadWithOneOffFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up input payload with one_off frequency and explicit date range
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'one_off',
'start_date_period' => '2025-07-20 00:00:00',
'end_date_period' => '2025-07-30 00:00:00',
'call_type' => ['conference'],
'media_types' => ['pdf'],
'min_call_duration' => 15,
'max_call_duration' => 120,
'min_deal_value' => 5000,
'max_deal_value' => 50000,
'additional_prompt_input' => 'Custom Prompt',
];
// Call the method directly
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result contains the expected fields
$this->assertArrayHasKey('team_id', $result);
$this->assertArrayHasKey('report_type', $result);
$this->assertArrayHasKey('from_date', $result);
$this->assertArrayHasKey('to_date', $result);
$this->assertArrayHasKey('call_types', $result);
$this->assertArrayHasKey('call_duration_min_seconds', $result);
$this->assertArrayHasKey('call_duration_max_seconds', $result);
$this->assertArrayHasKey('deal_min_value', $result);
$this->assertArrayHasKey('deal_max_value', $result);
$this->assertArrayHasKey('special_requirements', $result);
// Assert specific values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals(['conference'], $result['call_types']);
$this->assertEquals(15 * 60, $result['call_duration_min_seconds']);
$this->assertEquals(120 * 60, $result['call_duration_max_seconds']);
$this->assertEquals(5000, $result['deal_min_value']);
$this->assertEquals(50000, $result['deal_max_value']);
$this->assertEquals('Custom Prompt', $result['special_requirements']);
// Verify date formats (RFC3339)
$fromDate = Carbon::parse('2025-07-20 00:00:00');
$toDate = Carbon::parse('2025-07-30 00:00:00');
$this->assertEquals($fromDate->format(DateTimeInterface::RFC3339), $result['from_date']);
$this->assertEquals($toDate->format(DateTimeInterface::RFC3339), $result['to_date']);
}
public function testGetActivitiesCountPayloadWithWeeklyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with weekly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'weekly',
'call_type' => ['conference', 'dialer'],
'media_types' => ['pdf', 'podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Get the expected date range for weekly frequency
// Weekly: subWeeks(1).startOfDay() to subDay().endOfDay()
$now = Carbon::now();
$fromDate = $now->copy()->subWeeks(1)->startOfDay();
$toDate = $now->copy()->subDay()->endOfDay();
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
$this->assertEquals(['conference', 'dialer'], $result['call_types']);
$this->assertEquals([], $result['call_deal_stage']);
$this->assertEquals([], $result['current_deal_stage']);
$this->assertNull($result['deal_min_value']);
$this->assertNull($result['deal_max_value']);
$this->assertNull($result['call_duration_min_seconds']);
$this->assertNull($result['call_duration_max_seconds']);
$this->assertNull($result['special_requirements']);
$this->assertNull($result['request_id']);
$this->assertNull($result['callback_url']);
// Check exact dates with full calendar days
$this->assertEquals('2025-08-05T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2025-08-11T23:59:59+00:00', $result['to_date']);
Carbon::setTestNow();
}
public function testGetActivitiesCountPayloadWithMonthlyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with monthly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'monthly',
'media_types' => ['podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
// Check that dates are in RFC3339 format
// Use assertStringStartsWith to avoid exact timestamp comparison issues
$this->assertStringStartsWith('2025-07', $result['from_date']);
$this->assertStringStartsWith('2025-08-11', $result['to_date']);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
There were 8 failures:
1) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75413
|
|
75419
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
testGetActivitiesCountPayloadWithWeeklyFrequency
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/1
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use DateTimeInterface;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Group;
use Jiminny\Models\Team;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceActivitiesCountTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Group $group;
protected function setUp(): void
{
parent::setUp();
// Create a mock team
$team = $this->createMock(Team::class);
$team->method('getId')->willReturn(123);
$team->method('getUuid')->willReturn('6473c918-d8db-4ded-a52b-4febfd7b7c02');
$team->method('hasFeature')
->with(FeatureEnum::AUTOMATED_REPORTS)
->willReturn(true);
// Create a mock group
$this->group = $this->createMock(Group::class);
$this->group->method('getId')->willReturn(1);
$this->group->method('getTeamId')->willReturn(123);
// Create mocks for dependencies
$teamRepository = $this->createMock(TeamRepository::class);
$teamRepository->method('idOrUuid')
->willReturn($team);
$groupRepository = $this->createMock(GroupRepository::class);
$groupRepository->method('findByUuid')
->willReturnCallback(function ($uuid) {
if ($uuid === '0cdece0a-7b12-49ae-8b7e-366bbb6d42d2') {
return $this->group;
}
return null;
});
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create a real service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$automatedReportsRepository,
$webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
public function testGetActivitiesCountPayloadWithOneOffFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up input payload with one_off frequency and explicit date range
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'one_off',
'start_date_period' => '2025-07-20 00:00:00',
'end_date_period' => '2025-07-30 00:00:00',
'call_type' => ['conference'],
'media_types' => ['pdf'],
'min_call_duration' => 15,
'max_call_duration' => 120,
'min_deal_value' => 5000,
'max_deal_value' => 50000,
'additional_prompt_input' => 'Custom Prompt',
];
// Call the method directly
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result contains the expected fields
$this->assertArrayHasKey('team_id', $result);
$this->assertArrayHasKey('report_type', $result);
$this->assertArrayHasKey('from_date', $result);
$this->assertArrayHasKey('to_date', $result);
$this->assertArrayHasKey('call_types', $result);
$this->assertArrayHasKey('call_duration_min_seconds', $result);
$this->assertArrayHasKey('call_duration_max_seconds', $result);
$this->assertArrayHasKey('deal_min_value', $result);
$this->assertArrayHasKey('deal_max_value', $result);
$this->assertArrayHasKey('special_requirements', $result);
// Assert specific values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals(['conference'], $result['call_types']);
$this->assertEquals(15 * 60, $result['call_duration_min_seconds']);
$this->assertEquals(120 * 60, $result['call_duration_max_seconds']);
$this->assertEquals(5000, $result['deal_min_value']);
$this->assertEquals(50000, $result['deal_max_value']);
$this->assertEquals('Custom Prompt', $result['special_requirements']);
// Verify date formats (RFC3339)
$fromDate = Carbon::parse('2025-07-20 00:00:00');
$toDate = Carbon::parse('2025-07-30 00:00:00');
$this->assertEquals($fromDate->format(DateTimeInterface::RFC3339), $result['from_date']);
$this->assertEquals($toDate->format(DateTimeInterface::RFC3339), $result['to_date']);
}
public function testGetActivitiesCountPayloadWithWeeklyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with weekly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'weekly',
'call_type' => ['conference', 'dialer'],
'media_types' => ['pdf', 'podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Get the expected date range for weekly frequency
// Weekly: subWeeks(1).startOfDay() to subDay().endOfDay()
$now = Carbon::now();
$fromDate = $now->copy()->subWeeks(1)->startOfDay();
$toDate = $now->copy()->subDay()->endOfDay();
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
$this->assertEquals(['conference', 'dialer'], $result['call_types']);
$this->assertEquals([], $result['call_deal_stage']);
$this->assertEquals([], $result['current_deal_stage']);
$this->assertNull($result['deal_min_value']);
$this->assertNull($result['deal_max_value']);
$this->assertNull($result['call_duration_min_seconds']);
$this->assertNull($result['call_duration_max_seconds']);
$this->assertNull($result['special_requirements']);
$this->assertNull($result['request_id']);
$this->assertNull($result['callback_url']);
// Check exact dates with full calendar days
$this->assertEquals('2025-08-05T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2025-08-11T23:59:59+00:00', $result['to_date']);
Carbon::setTestNow();
}
public function testGetActivitiesCountPayloadWithMonthlyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with monthly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'monthly',
'media_types' => ['podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
// Check that dates are in RFC3339 format
// Use assertStringStartsWith to avoid exact timestamp comparison issues
$this->assertStringStartsWith('2025-07', $result['from_date']);
$this->assertStringStartsWith('2025-08-11', $result['to_date']);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
There were 8 failures:
1) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75419
|
|
75525
|
FirefoxFileEditViewHistoryBookmarksProfilesToolsWi FirefoxFileEditViewHistoryBookmarksProfilesToolsWindowHelpDOCKER (docker-compose)APP (-zsh)DOCKER181DEV (-zsh)• ₴211DOCKER (docker-compose)"e802ad473a4f""message""loadedmodule [x-pack-rollup]" }elasticsearch1 {"type":"server""timestamp" :NFO""2026-04-24T06:33:37,337Z""component": "o.e.p.PluginsService""cluster.name" :"docker-cluster""level": "I"node. name": "e802ad473a4f""message":"loaded module [x-pack-security]" }elasticsearchNFO",1 {"type":"server""timestamp":"component":"o.e.p.PluginsService""2026-04-24T06:33:37,337Z""cluster.name":"docker-cluster""level""node.name": "e802ad473a4f""message":"loaded module [x-pack-sql]"}elasticsearch1 {"type""server""timestamp"NFO","component":"o.e.p.PluginsService""2026-04-24T06:33:37,337Z""cluster.name" :"docker-cluster""level":"I"node. name": "e802ad473a4f""message":"loaded module [x-pack-stack]" }elasticsearch1 {"type""server""timestamp" :NFO","component":"o.e.p.PluginsService""2026-04-24T06:33:37,337Z""cluster.name":"docker-cluster""level": "I"node. name": "e802ad473a4f""message":"loadedmodule [x-pack-voting-only-node]" }elasticsearch1 {"type":"server""timestamp" :"2026-04-24T06:33:37,337Z"NFO""component" :"o.e.p.PluginsService""cluster.name":"docker-cluster""level":"I"node.name":"e802ad473a4f""message":"Loaded module [x-pack-watcher]" }elasticsearchNFO",I {"type":"server""timestamp" :"2026-04-24T06:33:37,338Z""level": "I"component": ".e.p.PluginsService","cluster.name": "docker-cluster", "node.name": "e802ad473a4f", "message": "no plugins loaded" }elasticsearchI {"type": "deprecation", "timestamp": "2026-04-24T06:33:37,513Z", "level": "DEPRECATION","component":"o.e.d.c.s.Settings","cluster.name":"docker-cluster"node. name":"e802ad473a4f", "message":"[node.data] setting was deprecated in Elasticsearch and will be removed in a future release! See the breaking changes documentation forthe next major version." }elasticsearch | {"type": "server""timestamp":"2026-04-24T06:33:37,541Z", "level": "INFO", "component": "o.e.e.NodeEnvironment""cluster.name":": "e802ad473a4f", "message": "using [1] data paths, mounts [[/usr/share/elasticsearch/data (/dev/vda1)]], net usable_space [14.4gb], net total_space [58.3gb], types [ext4]" }elasticsearch1 {"type": "server""timestamp":NFO""component": "o.e.e.NodeEnvironment""2026-04-24T06:33:37,542Z""cluster.name":"docker-cluster","level": "I"node. name":"e80Zad473a4f", "message": "heap size [700mb], compressed ordinary object pointers [true]"}elasticsearch1 {"type": "server", "timestamp": "2026-04-24T06:33:37,813Z", "level":"INFO""component": "o.e.n.Node""cluster.name": "docker-cluster""node.name": "e802ad473a4f""message": "node name [e802ad473a4f], node ID [e2ZKzgw4Q4aCf2w51jWr1A], clustername [docker-cluster], roles [transform, master, remote_cluster_client, data, ml, data_cdata_hot, data_warm, data_cold, ingest]" }elasticsearch | {"type": "server""timestamp":"2026-04-24T06:33:46,012Z","I"component": "o.e.x.m.p.1.CppLogMessageHandler","cluster.name":"node.name": "e802ad473a4f","message":"[controller/214] [Main.cc@114] controller (64bit): Version 7.10.2 (Build 40a3af639d4698) Copyright (c) 2020 Elasticsearch BV" }§ Daily - Platform • in 12 mA100% <sqlite3Fri 24 Apr 9:33:47181* *583screenpipe"PROD (-zsh)Last login: Thu Apr 23 12:23:42 on ttys010• 84Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ |X L3 EU (-zsh)Last login: Thu Apr 23 12:23:42 on ttys010Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parents-ukas-Kovaliks-MacBook-Pro-Jiminny~$ IX T4STAGE (-zsh)Last login: Thu Apr 23 12:23:42 on ttys010Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetrycould not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny$T5QA (-zsh)Last login: Thu Apr 23 12:23:42on ttys010Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.tomlfile in /Users/lukas or its parentsX T6 FE (-zsh)Last login: Thu Apr 23 12:23:42 on ttys010Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry couldnotfind a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ IEXTPoetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry couldnot find a pyproject.tomlfile in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ [|PRODSTAGEFRONTENDEXTENSIONView in Docker Desktopo View ConfigEnable Watch...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75525
|
|
75526
|
HomdDMSActivityMorePhpStormViewINavigareCodeLarave HomdDMSActivityMorePhpStormViewINavigareCodeLaravelRefactorTOOISWindowFV faVsco.js#12011 on JY-20157-AJ-report-not-send-notificatiorProiect© TrackAutomatedReportGeneratedEventTest.php© SendReportNotGeneratedMailJobTest.phpJiminny ... ~# c-learning-peopie# confusion-cli# curiosity lal# engineering9 24# deal-insights# engineeringifrontend# general# infra-change#jiminny-bg• people-with• people-with# platform-te:# platform-tick# product laur# random# releases# sofia-office# support# thank-yous#t the neonle&.- Direct messageR Stefka StoyaAdelina Petro8. Vasil VasilevStoyan Tom% Galya Dimit3 Aneliva Angef Nilolov VonlestFavourites> # Jazyky#recycleSyncDatav ccreenninearchive.db> *2026-04-151n lukas•2026-04-16• Р 2026.01-17• iCloud Drive>2026-04-21• 2026-04-22D DXP4800PLUS-B5Fscreenpipe-day.shscreenpipe.db48 Networ!• CRM• Orangel• Red• Yellow• Green› = Skola> Computer Scienceelnhuntu-24.04 4_live-cerver-amd64icd> * CODEGitaraa start machine wavlocation-history.isor1910229e6394bdc9...2141382106f.uc• Purple() All Taas.M Hubsnot> D IntegrationAppM Listeners› • Pipedrive•M Calecfarco> @ Fields> @ OpportunityMatcher> @ OpportunitySyncStrategy> O ProspectSearchStrategy> 0 ServiceTraits© ClientTest.php© DecorateActivityTest.phpC DeleteObjectsTraitTest.phpc) rieldDerinitions| est.onp© GetActivitvFieldNameTest.phPavloadBuilderTest.phpQueryBuilderTest.phpC @uervHandlerTest.php© QuerylteratorTest.php© QueryResultsTests.phpC ServiceTest.php© SyncBatchRedisServiceTest.|© BaseServiceTest.php© CachedCrmServiceDecoratorTe© CrmActivityServiceTest.phpc CrmConfiaurationSettinacCervid© CrmObjectsResolverTest.php© DefaultProspectSearchStrategyEmailHelperTest.php© FieldValueConverterTest.php© LayoutManagerTest.php© MigrateProviderServiceTest.php 34© OpportunityActivityMatcherTest© OpportunitySyncStrategyResolv© ProspectCacheTest.php© ProspectSearchStrateayFactorv© ProviderRegistryTest.php© RecordSelectorTest.php98 0 >C ResolveCompanyNameByEmailT 152© TimePerioditeratorTest.php153 DC UndateCrmDataResolverTest.ph 154>MInternallvMKioskv AutomatedRenortsC) ActivitvivoeServiceTest.oho 158@ Ask JiminnvReportActivitvSer(C) AutomatedReportsCommana lest.onpAutomateakeporscommana.ongAskJiminnykeportActivityservice.ong© Automatedreporisservicelest.phgortsserviceActivitiescountl est.php xC) ReportWithAttachment.phoC) RequestGenerateAskJiminnyReportJob.oho(C) SendReportNotGeneratedMailJob.phoC) SendReportMailJob.php© TranscriptionAiSummaryReadyEvent.php(C) AutomatedReportGenerated.ohvC) CreateDialerActivityEvent.oho(C) CreateNudgeCreatedeventonp© CreateActivityLoggedEvent.php© TrackProviderInstalledEvent.phpC UserPilotActivityListener.ong(C) UserPilotClientonp(C) PlanhatActivityListener.onpphp api_v2.php(C) AutomatedReportResult.phptestGetActivitiesCountPaylc x 5 CcWTLT:ohddeclare(strict tvnes=):namesnace Tests Unit Services Klosk AutomatedRenorts:use ..•class AutomatedReportsServiceActivitiesCountTest extends TestCase• private AutomatedReportsService Sservice:4 usagesprivate Group Sgroupprotected function setUpO: void{...}public function testGetActivitiesCountPayloadWithOne0ffFrequencyO: void{...}public functiontestbetact1v1t1escountPavloadW1thWeeklyFrequency: vo1d// Freeze time for predictable date handlingCarbon::setTestNow(Carbon::parse( time12025-08-12 18:30:00')):(c) Ask JiminnvRenortServiceTes 161Set un minimal inout pavload with weekly freauencvSinoutPavload = П'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02' ,"penont enahledi => +rue.iteamsi = 1'Acdecola-7h12-/0ap-8h7p-366hhh6d42d2111ch AutomatedRenortcServicoSie 16te lofAadits *> 'weekly'."call tvnel => "'confenence!]"Dally - Platrorm • In 12m100% CFri 24 Apr 9:33:47= custom.log X = laravel.logA SF [jiminny@localhost]# hs_local yiminny @localnost« console (PROD)© DealsRepository.php# console [eu)A console [STAGING]There were 8 failures:1) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayLoadWithWeeklyFrequeFailed asserting that two strings are egual.--- Expected[dd dc- '2025-08-05T00:00:00+00:00+ 2025-08-03100:00:00+00:00Jhome/cinclect/ononect/tests/un1t/Seryices/Krosk/AutomatedRenonts/AutomatedRenontsServ1ceAct1v1t1escountllest.ono.9l2) Tests\Unit\Services Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayLoadWithMonthLyFrequFailed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11" .home/circleci/oroiect/tests/Unit/Services/Klosk/AutomatedRenorts/AutomatedRenortsServiceActivitiesCounttest.oho:223) Tests\Unit\Services Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequenFailed asserting that two strings are equal.*== Cxoecteu+++ Actualld 00-12022-02-08 00-00-001+^ 2023-02-05 00:00:00'/home/circleci/proiect/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:1864) Tests Unit\Services Kiosk AutchatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequenFailed asserting that two strings are equal.--- Expected+++ Actualdid dd- 2023-01-15 00:00:00'+'2023-01-01 00:00:00home/circleci/oronect/tests/Unit/Services/Kiosk/AutomatedReoorts/AutomatedRenortsServiceReportGenerationTest.ono: 2055-estsuntServi ces KGiosk VAutomat edRenonts VAutomatedRenontsServiceRenont cenena on estratestca l cu late comAndi o bat ewithouanten veneatFailed asserting that two strings are equal,--- Exnectedi+++ Actualc ce-'2022-11-15 00:00:00'412022-10-01 00-00-001Whomo/cindlocinaninat/tostc/Mnit/Soaviicos/Minc/AutomatodPonontc/AutomatodPonantcSoavilcoRonontfononat.ionTost.nhn .2206) Tests\Unit\Services Kiosk AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequeFailed asserting that two strings are equal.--- SynostodlW Windsurf Teams 34:46 UTF-8 Po 4 space...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75526
|
|
75528
|
HomdDMSActivityMorePhpStormViewINavigareCodeLarave HomdDMSActivityMorePhpStormViewINavigareCodeLaravelKeractorTOOISWindowFV faVsco.js#12011 on JY-20157-AJ-report-not-send-notificatiorProiectv© TrackAutomatedReportGeneratedEventTest.phg© SendReportNotGeneratedMailJobTest.phpJiminny ... ~# c-learning-peopie# confusion-cl8 24# engineeringic curiosity al# deal-insights# engineering* frontend# general# infra-change#jiminny-bg• people-with• people-with# platform-te:# platform-tick# product laur# random# releases# sofia-office# support# thank-yous#t the neonle&.- Direct messageR Stefka StoyaAdelina Petro8. Vasil VasilevStoyan Tom% Galya Dimit3 Aneliva Angef Nilolov Vonlestravounte:> # Jazyky#recycleSyncDatav ccreenninearchive.db> *2026-04-15•2026-04-16• Р 2026.01-17• iCloud Drive999 Svnc foldel>2026-04-21• 2026-04-22D DXP4800PLUS-B5F48 Networ!screenpipe-day.shscreenpipe.db• CRM• Orangel• Red• Yellow• Green› = Skola> Computer Scienceelnhuntu-24.04 4_live-cerver-amd64ic/> * CODEGitaraa start machine wavlocation-history.isonlocation-history(1).json1910229e6394bdc9..2141382106f.ug• Purple0) All Taas.M Hubsnot> D IntegrationAppM Listeners› • Pipedrive•M Calecfarco> @ Fields> @ OpportunityMatcher> @ OpportunitySyncStrategy> O ProspectSearchStrategy> 0 ServiceTraits© ClientTest.php© DecorateActivityTest.phpC DeleteObjectsTraitTest.phpc) rieldDerinitions| est.onp© GetActivitvFieldNameTest.phPavloadBuilderTest.phpQueryBuilderTest.php(c) @uerv-andlertest.ono© QuerylteratorTest.php© QueryResultsTests.phpC ServiceTest.php© SyncBatchRedisServiceTest.|© BaseServiceTest.php© CachedCrmServiceDecoratorTe© CrmActivityServiceTest.phpc CrmConfiaurationSettinacCervid© CrmObjectsResolverTest.php© DefaultProspectSearchStrategyEmailHelperTest.php© FieldValueConverterTest.php© LayoutManagerTest.php© MigrateProviderServiceTest.php© OpportunityActivityMatcherTest© OpportunitySyncStrategyResolv© ProspectCacheTest.php© ProspectSearchStrateayFactory© ProviderRegistryTest.php© RecordSelectorTest.phpC ResolveCompanyNameByEmailT 152© TimePerioditeratorTest.phpC UndateCrmDataResolverTest.oh 154>M InternallvMKioskv AutomatedRenortsC) ActivitvivoeServiceTest.oho 158@ Ask JiminnvReportActivitvSer(c) Ask JiminnvRenortServiceTes 161(C) AutomatedReportsCommana lest.onpAutomateakeporscommana.ongAskJiminnykeportActivityservice.ong© Automatedreporisservicelest.phgortsserviceActivitiescountl est.php xC) ReportWithAttachment.phoC) RequestGenerateAskJiminnyReportJob.oho(C) SendReportNotGeneratedMailJob.phoC) SendReportMailJob.php(C) TranscriotionA SummarvReadvEvent.oho(C) AutomatedReportGenerated.ohvC) CreateDialerActivityEvent.oho(C) CreateNudgeCreatedeventonp© CreateActivityLoggedEvent.php© TrackProviderInstalledEvent.phpC UserPilotActivityListener.ong(C) UserPilotClientonp(C) PlanhatActivityListener.onpphp api_v2.php(C) AutomatedReportResult.phptestGetActivitiesCountPaylc x 5 Cc W.*TLT:ohddeclare(strict tvnes=):namesnace Tests Unit Services Klosk AutomatedRenorts:use ..•ZADclass AutomatedReportsServiceActivitiesCountTest extends TestCaseuse TestPrivateMethod.4 usagesprivate AutomatedReportsService Sservice:4 usagesprivate Group Sgroupprotected function setUpO: void{...}98 0 >153 Dpublic function testGetActivitiesCountPayloadWithOne0ffFrequencyO: void{...}public functiontestbetact1v1t1escountPavloadW1thWeeklyFrequency: vo1d// Freeze time for predictable date handlingCarbon::setTestNow(Carbon::parse( time12025-08-12 18:30:00')):Set un minimal inout pavload with weekly freauencvSinoutPavload = П'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02' ,"penont enahledi => +rue.iteamsi = 1'Acdecola-7h12-/0ap-8h7p-366hhh6d42d2111ch AutomatedRenortcServicoSie 16'weekly',=36"Dally - Platrorm • In 12m100% CFri 24 Apr 9:33:56= custom.log X = laravel.logA SF [jiminny@localhost]# hs_local yiminny @localnost« console (PROD)© DealsRepository.php# console [eu)A console [STAGING]There were 8 failures:1) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayLoadWithWeeklyFrequeFailed asserting that two strings are equal.--- Expected[dd dc-'2025-08-05T00:00:00+00:00'+'2025-08-03T00:00:00+00:00'Jhome/cinclect/ononect/tests/unt/Serynces/Kosk/AutomatedRenonts/AutomatedRenontsSerysceActy1tnescountllest.oho.9l2) Tests\Unit\Services Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayLoadWithMonthLyFrequFailed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11" .home/circleci/oroiect/tests/Unit/Services/Klosk/AutomatedRenorts/AutomatedRenortsServiceActivitiesCounttest.oho:223) Tests\Unit\Services Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequenFailed asserting that two strings are equal,*== Cxoecteu+++ Actualld 00-12022-02-08 00-00-001+^ 2023-02-05 00:00:00'/home/circleci/proiect/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:1864) Tests Unit\Services Kiosk AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequenFailed asserting that two strings are equal.--- Expected+++ Actualdid dd- 2023-01-15 00:00:00'+'2023-01-01 00:00:00home/circleci/oronect/tests/Unit/Services/Kiosk/AutomatedReoorts/AutomatedRenortsServiceReportGenerationTest.ono: 2055-estsuntServi ces KGiosk VAutomat edRenonts VAutomatedRenontsServiceRenont cenena on estratestca l cu late comAndi o bat ewithouanten veneatFailed asserting that two strings are equal,--- Exnectedi+++ Actual00 аC-'2022-11-15 00:00:00'412022-10-01 00-00-001Whomo/cindlocinaninat/tostc/Mnit/Soaviicos/Minc/AutomatodPonontc/AutomatodPonantcSoavilcoRonontfononat.ionTost.nhn .2206) Tests\Unit\Services Kiosk AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequeFailed asserting that two strings are equal.--- SynostodlW Windsurf Teams 29:11 UTF-8 Po 4 space....
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75528
|
|
75529
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
testGetActivitiesCountPayloadWithWeeklyFrequency
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/1
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use DateTimeInterface;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Group;
use Jiminny\Models\Team;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceActivitiesCountTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Group $group;
protected function setUp(): void
{
parent::setUp();
// Create a mock team
$team = $this->createMock(Team::class);
$team->method('getId')->willReturn(123);
$team->method('getUuid')->willReturn('6473c918-d8db-4ded-a52b-4febfd7b7c02');
$team->method('hasFeature')
->with(FeatureEnum::AUTOMATED_REPORTS)
->willReturn(true);
// Create a mock group
$this->group = $this->createMock(Group::class);
$this->group->method('getId')->willReturn(1);
$this->group->method('getTeamId')->willReturn(123);
// Create mocks for dependencies
$teamRepository = $this->createMock(TeamRepository::class);
$teamRepository->method('idOrUuid')
->willReturn($team);
$groupRepository = $this->createMock(GroupRepository::class);
$groupRepository->method('findByUuid')
->willReturnCallback(function ($uuid) {
if ($uuid === '0cdece0a-7b12-49ae-8b7e-366bbb6d42d2') {
return $this->group;
}
return null;
});
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create a real service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$automatedReportsRepository,
$webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
public function testGetActivitiesCountPayloadWithOneOffFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up input payload with one_off frequency and explicit date range
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'one_off',
'start_date_period' => '2025-07-20 00:00:00',
'end_date_period' => '2025-07-30 00:00:00',
'call_type' => ['conference'],
'media_types' => ['pdf'],
'min_call_duration' => 15,
'max_call_duration' => 120,
'min_deal_value' => 5000,
'max_deal_value' => 50000,
'additional_prompt_input' => 'Custom Prompt',
];
// Call the method directly
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result contains the expected fields
$this->assertArrayHasKey('team_id', $result);
$this->assertArrayHasKey('report_type', $result);
$this->assertArrayHasKey('from_date', $result);
$this->assertArrayHasKey('to_date', $result);
$this->assertArrayHasKey('call_types', $result);
$this->assertArrayHasKey('call_duration_min_seconds', $result);
$this->assertArrayHasKey('call_duration_max_seconds', $result);
$this->assertArrayHasKey('deal_min_value', $result);
$this->assertArrayHasKey('deal_max_value', $result);
$this->assertArrayHasKey('special_requirements', $result);
// Assert specific values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals(['conference'], $result['call_types']);
$this->assertEquals(15 * 60, $result['call_duration_min_seconds']);
$this->assertEquals(120 * 60, $result['call_duration_max_seconds']);
$this->assertEquals(5000, $result['deal_min_value']);
$this->assertEquals(50000, $result['deal_max_value']);
$this->assertEquals('Custom Prompt', $result['special_requirements']);
// Verify date formats (RFC3339)
$fromDate = Carbon::parse('2025-07-20 00:00:00');
$toDate = Carbon::parse('2025-07-30 00:00:00');
$this->assertEquals($fromDate->format(DateTimeInterface::RFC3339), $result['from_date']);
$this->assertEquals($toDate->format(DateTimeInterface::RFC3339), $result['to_date']);
}
public function testGetActivitiesCountPayloadWithWeeklyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with weekly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'weekly',
'call_type' => ['conference', 'dialer'],
'media_types' => ['pdf', 'podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Get the expected date range for weekly frequency
// Weekly: previous full calendar week (Sunday - Saturday)
$now = Carbon::now();
$fromDate = $now->copy()->subWeek()->startOfWeek();
$toDate = $now->copy()->subWeek()->endOfWeek();
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
$this->assertEquals(['conference', 'dialer'], $result['call_types']);
$this->assertEquals([], $result['call_deal_stage']);
$this->assertEquals([], $result['current_deal_stage']);
$this->assertNull($result['deal_min_value']);
$this->assertNull($result['deal_max_value']);
$this->assertNull($result['call_duration_min_seconds']);
$this->assertNull($result['call_duration_max_seconds']);
$this->assertNull($result['special_requirements']);
$this->assertNull($result['request_id']);
$this->assertNull($result['callback_url']);
// Check exact dates with full calendar days (previous calendar week)
$this->assertEquals('2025-08-03T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2025-08-09T23:59:59+00:00', $result['to_date']);
Carbon::setTestNow();
}
public function testGetActivitiesCountPayloadWithMonthlyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with monthly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'monthly',
'media_types' => ['podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
// Check that dates are in RFC3339 format
// Use assertStringStartsWith to avoid exact timestamp comparison issues
$this->assertStringStartsWith('2025-07-01', $result['from_date']);
$this->assertStringStartsWith('2025-07-31', $result['to_date']);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
There were 8 failures:
1) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75529
|
|
75530
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
testGetActivitiesCountPayloadWithWeeklyFrequency
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/1
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
3
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use DateTimeInterface;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Group;
use Jiminny\Models\Team;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceActivitiesCountTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Group $group;
protected function setUp(): void
{
parent::setUp();
// Create a mock team
$team = $this->createMock(Team::class);
$team->method('getId')->willReturn(123);
$team->method('getUuid')->willReturn('6473c918-d8db-4ded-a52b-4febfd7b7c02');
$team->method('hasFeature')
->with(FeatureEnum::AUTOMATED_REPORTS)
->willReturn(true);
// Create a mock group
$this->group = $this->createMock(Group::class);
$this->group->method('getId')->willReturn(1);
$this->group->method('getTeamId')->willReturn(123);
// Create mocks for dependencies
$teamRepository = $this->createMock(TeamRepository::class);
$teamRepository->method('idOrUuid')
->willReturn($team);
$groupRepository = $this->createMock(GroupRepository::class);
$groupRepository->method('findByUuid')
->willReturnCallback(function ($uuid) {
if ($uuid === '0cdece0a-7b12-49ae-8b7e-366bbb6d42d2') {
return $this->group;
}
return null;
});
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create a real service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$automatedReportsRepository,
$webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
public function testGetActivitiesCountPayloadWithOneOffFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up input payload with one_off frequency and explicit date range
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'one_off',
'start_date_period' => '2025-07-20 00:00:00',
'end_date_period' => '2025-07-30 00:00:00',
'call_type' => ['conference'],
'media_types' => ['pdf'],
'min_call_duration' => 15,
'max_call_duration' => 120,
'min_deal_value' => 5000,
'max_deal_value' => 50000,
'additional_prompt_input' => 'Custom Prompt',
];
// Call the method directly
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result contains the expected fields
$this->assertArrayHasKey('team_id', $result);
$this->assertArrayHasKey('report_type', $result);
$this->assertArrayHasKey('from_date', $result);
$this->assertArrayHasKey('to_date', $result);
$this->assertArrayHasKey('call_types', $result);
$this->assertArrayHasKey('call_duration_min_seconds', $result);
$this->assertArrayHasKey('call_duration_max_seconds', $result);
$this->assertArrayHasKey('deal_min_value', $result);
$this->assertArrayHasKey('deal_max_value', $result);
$this->assertArrayHasKey('special_requirements', $result);
// Assert specific values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals(['conference'], $result['call_types']);
$this->assertEquals(15 * 60, $result['call_duration_min_seconds']);
$this->assertEquals(120 * 60, $result['call_duration_max_seconds']);
$this->assertEquals(5000, $result['deal_min_value']);
$this->assertEquals(50000, $result['deal_max_value']);
$this->assertEquals('Custom Prompt', $result['special_requirements']);
// Verify date formats (RFC3339)
$fromDate = Carbon::parse('2025-07-20 00:00:00');
$toDate = Carbon::parse('2025-07-30 00:00:00');
$this->assertEquals($fromDate->format(DateTimeInterface::RFC3339), $result['from_date']);
$this->assertEquals($toDate->format(DateTimeInterface::RFC3339), $result['to_date']);
}
public function testGetActivitiesCountPayloadWithWeeklyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with weekly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'weekly',
'call_type' => ['conference', 'dialer'],
'media_types' => ['pdf', 'podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Get the expected date range for weekly frequency
// Weekly: previous full calendar week (Sunday - Saturday)
$now = Carbon::now();
$fromDate = $now->copy()->subWeek()->startOfWeek();
$toDate = $now->copy()->subWeek()->endOfWeek();
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
$this->assertEquals(['conference', 'dialer'], $result['call_types']);
$this->assertEquals([], $result['call_deal_stage']);
$this->assertEquals([], $result['current_deal_stage']);
$this->assertNull($result['deal_min_value']);
$this->assertNull($result['deal_max_value']);
$this->assertNull($result['call_duration_min_seconds']);
$this->assertNull($result['call_duration_max_seconds']);
$this->assertNull($result['special_requirements']);
$this->assertNull($result['request_id']);
$this->assertNull($result['callback_url']);
// Check exact dates with full calendar days (previous calendar week)
$this->assertEquals('2025-08-03T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2025-08-09T23:59:59+00:00', $result['to_date']);
Carbon::setTestNow();
}
public function testGetActivitiesCountPayloadWithMonthlyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with monthly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'monthly',
'media_types' => ['podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
// Check that dates are in RFC3339 format
// Use assertStringStartsWith to avoid exact timestamp comparison issues
$this->assertStringStartsWith('2025-07-01', $result['from_date']);
$this->assertStringStartsWith('2025-07-31', $result['to_date']);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
There were 8 failures:
1) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75530
|
|
75531
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
testGetActivitiesCountPayloadWithWeeklyFrequency
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/1
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use DateTimeInterface;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Group;
use Jiminny\Models\Team;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceActivitiesCountTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Group $group;
protected function setUp(): void
{
parent::setUp();
// Create a mock team
$team = $this->createMock(Team::class);
$team->method('getId')->willReturn(123);
$team->method('getUuid')->willReturn('6473c918-d8db-4ded-a52b-4febfd7b7c02');
$team->method('hasFeature')
->with(FeatureEnum::AUTOMATED_REPORTS)
->willReturn(true);
// Create a mock group
$this->group = $this->createMock(Group::class);
$this->group->method('getId')->willReturn(1);
$this->group->method('getTeamId')->willReturn(123);
// Create mocks for dependencies
$teamRepository = $this->createMock(TeamRepository::class);
$teamRepository->method('idOrUuid')
->willReturn($team);
$groupRepository = $this->createMock(GroupRepository::class);
$groupRepository->method('findByUuid')
->willReturnCallback(function ($uuid) {
if ($uuid === '0cdece0a-7b12-49ae-8b7e-366bbb6d42d2') {
return $this->group;
}
return null;
});
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create a real service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$automatedReportsRepository,
$webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
public function testGetActivitiesCountPayloadWithOneOffFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up input payload with one_off frequency and explicit date range
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'one_off',
'start_date_period' => '2025-07-20 00:00:00',
'end_date_period' => '2025-07-30 00:00:00',
'call_type' => ['conference'],
'media_types' => ['pdf'],
'min_call_duration' => 15,
'max_call_duration' => 120,
'min_deal_value' => 5000,
'max_deal_value' => 50000,
'additional_prompt_input' => 'Custom Prompt',
];
// Call the method directly
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result contains the expected fields
$this->assertArrayHasKey('team_id', $result);
$this->assertArrayHasKey('report_type', $result);
$this->assertArrayHasKey('from_date', $result);
$this->assertArrayHasKey('to_date', $result);
$this->assertArrayHasKey('call_types', $result);
$this->assertArrayHasKey('call_duration_min_seconds', $result);
$this->assertArrayHasKey('call_duration_max_seconds', $result);
$this->assertArrayHasKey('deal_min_value', $result);
$this->assertArrayHasKey('deal_max_value', $result);
$this->assertArrayHasKey('special_requirements', $result);
// Assert specific values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals(['conference'], $result['call_types']);
$this->assertEquals(15 * 60, $result['call_duration_min_seconds']);
$this->assertEquals(120 * 60, $result['call_duration_max_seconds']);
$this->assertEquals(5000, $result['deal_min_value']);
$this->assertEquals(50000, $result['deal_max_value']);
$this->assertEquals('Custom Prompt', $result['special_requirements']);
// Verify date formats (RFC3339)
$fromDate = Carbon::parse('2025-07-20 00:00:00');
$toDate = Carbon::parse('2025-07-30 00:00:00');
$this->assertEquals($fromDate->format(DateTimeInterface::RFC3339), $result['from_date']);
$this->assertEquals($toDate->format(DateTimeInterface::RFC3339), $result['to_date']);
}
public function testGetActivitiesCountPayloadWithWeeklyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with weekly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'weekly',
'call_type' => ['conference', 'dialer'],
'media_types' => ['pdf', 'podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Get the expected date range for weekly frequency
// Weekly: previous full calendar week (Sunday - Saturday)
$now = Carbon::now();
$fromDate = $now->copy()->subWeek()->startOfWeek();
$toDate = $now->copy()->subWeek()->endOfWeek();
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
$this->assertEquals(['conference', 'dialer'], $result['call_types']);
$this->assertEquals([], $result['call_deal_stage']);
$this->assertEquals([], $result['current_deal_stage']);
$this->assertNull($result['deal_min_value']);
$this->assertNull($result['deal_max_value']);
$this->assertNull($result['call_duration_min_seconds']);
$this->assertNull($result['call_duration_max_seconds']);
$this->assertNull($result['special_requirements']);
$this->assertNull($result['request_id']);
$this->assertNull($result['callback_url']);
// Check exact dates with full calendar days (previous calendar week)
$this->assertEquals('2025-08-03T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2025-08-09T23:59:59+00:00', $result['to_date']);
Carbon::setTestNow();
}
public function testGetActivitiesCountPayloadWithMonthlyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with monthly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'monthly',
'media_types' => ['podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
// Check that dates are in RFC3339 format
// Use assertStringStartsWith to avoid exact timestamp comparison issues
$this->assertStringStartsWith('2025-07-01', $result['from_date']);
$this->assertStringStartsWith('2025-07-31', $result['to_date']);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
There were 8 failures:
1) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75531
|
|
75533
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
testGetActivitiesCountPayloadWithWeeklyFrequency
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/1
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use DateTimeInterface;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Group;
use Jiminny\Models\Team;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceActivitiesCountTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Group $group;
protected function setUp(): void
{
parent::setUp();
// Create a mock team
$team = $this->createMock(Team::class);
$team->method('getId')->willReturn(123);
$team->method('getUuid')->willReturn('6473c918-d8db-4ded-a52b-4febfd7b7c02');
$team->method('hasFeature')
->with(FeatureEnum::AUTOMATED_REPORTS)
->willReturn(true);
// Create a mock group
$this->group = $this->createMock(Group::class);
$this->group->method('getId')->willReturn(1);
$this->group->method('getTeamId')->willReturn(123);
// Create mocks for dependencies
$teamRepository = $this->createMock(TeamRepository::class);
$teamRepository->method('idOrUuid')
->willReturn($team);
$groupRepository = $this->createMock(GroupRepository::class);
$groupRepository->method('findByUuid')
->willReturnCallback(function ($uuid) {
if ($uuid === '0cdece0a-7b12-49ae-8b7e-366bbb6d42d2') {
return $this->group;
}
return null;
});
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create a real service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$automatedReportsRepository,
$webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
public function testGetActivitiesCountPayloadWithOneOffFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up input payload with one_off frequency and explicit date range
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'one_off',
'start_date_period' => '2025-07-20 00:00:00',
'end_date_period' => '2025-07-30 00:00:00',
'call_type' => ['conference'],
'media_types' => ['pdf'],
'min_call_duration' => 15,
'max_call_duration' => 120,
'min_deal_value' => 5000,
'max_deal_value' => 50000,
'additional_prompt_input' => 'Custom Prompt',
];
// Call the method directly
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result contains the expected fields
$this->assertArrayHasKey('team_id', $result);
$this->assertArrayHasKey('report_type', $result);
$this->assertArrayHasKey('from_date', $result);
$this->assertArrayHasKey('to_date', $result);
$this->assertArrayHasKey('call_types', $result);
$this->assertArrayHasKey('call_duration_min_seconds', $result);
$this->assertArrayHasKey('call_duration_max_seconds', $result);
$this->assertArrayHasKey('deal_min_value', $result);
$this->assertArrayHasKey('deal_max_value', $result);
$this->assertArrayHasKey('special_requirements', $result);
// Assert specific values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals(['conference'], $result['call_types']);
$this->assertEquals(15 * 60, $result['call_duration_min_seconds']);
$this->assertEquals(120 * 60, $result['call_duration_max_seconds']);
$this->assertEquals(5000, $result['deal_min_value']);
$this->assertEquals(50000, $result['deal_max_value']);
$this->assertEquals('Custom Prompt', $result['special_requirements']);
// Verify date formats (RFC3339)
$fromDate = Carbon::parse('2025-07-20 00:00:00');
$toDate = Carbon::parse('2025-07-30 00:00:00');
$this->assertEquals($fromDate->format(DateTimeInterface::RFC3339), $result['from_date']);
$this->assertEquals($toDate->format(DateTimeInterface::RFC3339), $result['to_date']);
}
public function testGetActivitiesCountPayloadWithWeeklyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with weekly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'weekly',
'call_type' => ['conference', 'dialer'],
'media_types' => ['pdf', 'podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Get the expected date range for weekly frequency
// Weekly: previous full calendar week (Sunday - Saturday)
$now = Carbon::now();
$fromDate = $now->copy()->subWeek()->startOfWeek();
$toDate = $now->copy()->subWeek()->endOfWeek();
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
$this->assertEquals(['conference', 'dialer'], $result['call_types']);
$this->assertEquals([], $result['call_deal_stage']);
$this->assertEquals([], $result['current_deal_stage']);
$this->assertNull($result['deal_min_value']);
$this->assertNull($result['deal_max_value']);
$this->assertNull($result['call_duration_min_seconds']);
$this->assertNull($result['call_duration_max_seconds']);
$this->assertNull($result['special_requirements']);
$this->assertNull($result['request_id']);
$this->assertNull($result['callback_url']);
// Check exact dates with full calendar days (previous calendar week)
$this->assertEquals('2025-08-03T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2025-08-09T23:59:59+00:00', $result['to_date']);
Carbon::setTestNow();
}
public function testGetActivitiesCountPayloadWithMonthlyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with monthly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'monthly',
'media_types' => ['podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
// Check that dates are in RFC3339 format
// Use assertStringStartsWith to avoid exact timestamp comparison issues
$this->assertStringStartsWith('2025-07-01', $result['from_date']);
$this->assertStringStartsWith('2025-07-31', $result['to_date']);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
There were 8 failures:
1) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75533
|
|
75534
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
testGetActivitiesCountPayloadWithWeeklyFrequency
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/1
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use DateTimeInterface;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Group;
use Jiminny\Models\Team;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceActivitiesCountTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Group $group;
protected function setUp(): void
{
parent::setUp();
// Create a mock team
$team = $this->createMock(Team::class);
$team->method('getId')->willReturn(123);
$team->method('getUuid')->willReturn('6473c918-d8db-4ded-a52b-4febfd7b7c02');
$team->method('hasFeature')
->with(FeatureEnum::AUTOMATED_REPORTS)
->willReturn(true);
// Create a mock group
$this->group = $this->createMock(Group::class);
$this->group->method('getId')->willReturn(1);
$this->group->method('getTeamId')->willReturn(123);
// Create mocks for dependencies
$teamRepository = $this->createMock(TeamRepository::class);
$teamRepository->method('idOrUuid')
->willReturn($team);
$groupRepository = $this->createMock(GroupRepository::class);
$groupRepository->method('findByUuid')
->willReturnCallback(function ($uuid) {
if ($uuid === '0cdece0a-7b12-49ae-8b7e-366bbb6d42d2') {
return $this->group;
}
return null;
});
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create a real service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$automatedReportsRepository,
$webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
public function testGetActivitiesCountPayloadWithOneOffFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up input payload with one_off frequency and explicit date range
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'one_off',
'start_date_period' => '2025-07-20 00:00:00',
'end_date_period' => '2025-07-30 00:00:00',
'call_type' => ['conference'],
'media_types' => ['pdf'],
'min_call_duration' => 15,
'max_call_duration' => 120,
'min_deal_value' => 5000,
'max_deal_value' => 50000,
'additional_prompt_input' => 'Custom Prompt',
];
// Call the method directly
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result contains the expected fields
$this->assertArrayHasKey('team_id', $result);
$this->assertArrayHasKey('report_type', $result);
$this->assertArrayHasKey('from_date', $result);
$this->assertArrayHasKey('to_date', $result);
$this->assertArrayHasKey('call_types', $result);
$this->assertArrayHasKey('call_duration_min_seconds', $result);
$this->assertArrayHasKey('call_duration_max_seconds', $result);
$this->assertArrayHasKey('deal_min_value', $result);
$this->assertArrayHasKey('deal_max_value', $result);
$this->assertArrayHasKey('special_requirements', $result);
// Assert specific values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals(['conference'], $result['call_types']);
$this->assertEquals(15 * 60, $result['call_duration_min_seconds']);
$this->assertEquals(120 * 60, $result['call_duration_max_seconds']);
$this->assertEquals(5000, $result['deal_min_value']);
$this->assertEquals(50000, $result['deal_max_value']);
$this->assertEquals('Custom Prompt', $result['special_requirements']);
// Verify date formats (RFC3339)
$fromDate = Carbon::parse('2025-07-20 00:00:00');
$toDate = Carbon::parse('2025-07-30 00:00:00');
$this->assertEquals($fromDate->format(DateTimeInterface::RFC3339), $result['from_date']);
$this->assertEquals($toDate->format(DateTimeInterface::RFC3339), $result['to_date']);
}
public function testGetActivitiesCountPayloadWithWeeklyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with weekly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'weekly',
'call_type' => ['conference', 'dialer'],
'media_types' => ['pdf', 'podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Get the expected date range for weekly frequency
// Weekly: previous full calendar week (Sunday - Saturday)
$now = Carbon::now();
$fromDate = $now->copy()->subWeek()->startOfWeek();
$toDate = $now->copy()->subWeek()->endOfWeek();
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
$this->assertEquals(['conference', 'dialer'], $result['call_types']);
$this->assertEquals([], $result['call_deal_stage']);
$this->assertEquals([], $result['current_deal_stage']);
$this->assertNull($result['deal_min_value']);
$this->assertNull($result['deal_max_value']);
$this->assertNull($result['call_duration_min_seconds']);
$this->assertNull($result['call_duration_max_seconds']);
$this->assertNull($result['special_requirements']);
$this->assertNull($result['request_id']);
$this->assertNull($result['callback_url']);
// Check exact dates with full calendar days (previous calendar week)
$this->assertEquals('2025-08-03T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2025-08-09T23:59:59+00:00', $result['to_date']);
Carbon::setTestNow();
}
public function testGetActivitiesCountPayloadWithMonthlyFrequency(): void
{
// Freeze time for predictable date handling
Carbon::setTestNow(Carbon::parse('2025-08-12 18:30:00'));
// Set up minimal input payload with monthly frequency
$inputPayload = [
'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02',
'report_enabled' => true,
'teams' => ['0cdece0a-7b12-49ae-8b7e-366bbb6d42d2'],
'report_type' => 'exec_summary',
'frequency' => 'monthly',
'media_types' => ['podcast'],
];
// Call the method
$result = $this->service->getActivitiesCountPayload($inputPayload);
// Assert the result has expected values
$this->assertEquals(123, $result['team_id']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals([1], $result['group_ids']);
// Check that dates are in RFC3339 format
// Use assertStringStartsWith to avoid exact timestamp comparison issues
$this->assertStringStartsWith('2025-07-01', $result['from_date']);
$this->assertStringStartsWith('2025-07-31', $result['to_date']);
}
}
Code changed:
Hide
Sync Changes
Hide This Notification
There were 8 failures:
1) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75534
|
|
75538
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp§ Daily - Platform • in 10 m100% <Fri 24 Apr 9:35:10T81DEV (docker)APP (-zsh)DOCKERDEV (docker)$82*3root@docker_Lamp_1:/home/jiminny# php artisan jiminny: debugNow: 2026-04-22 12:21:40Now: 0From old:2026-04-15 00:00:00To old:2026-04-21 23:59:59From new: 2026-04-12 00:00:00To new: 2026-04-18 23:59:59root@docker_lamp_1:/home/jiminny# php artisan jiminny: debugNow: 2026-04-22 12:22:16Now: 0From old: 2026-03-22 00:00:00Toold: 2026-04-21 23:59:59From new: 2026-03-0100:00:00To new: 2026-03-31 23:59:59root@docker_lamp_1:/home/jiminny# php artisan jiminny:debugNow: 2026-04-22 12:22:32Now: 0From old: 2026-01-22 00:00:00To old: 2026-04-21 23:59:59From new: 2026-01-01 00:00:00To new: 2026-03-31 23:59:59root@docker_lamp_1:/home/jiminny#What's next:Try Docker Debug for seamless, persistent debugging tools in any container or image → docker debug 007d5da3af66Learn more at https://docs.docker.com/go/debug-cli/lukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~/jiminny/app (JY-20157-AJ-report-not-send-notification) $ devroot@docker_lamp_1:/home/jiminny# |screenpipe"• *4sqlite3DEV...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75538
|
|
75539
|
HomdDMSActivityMoreINavicatecode1001SQ Describe wh HomdDMSActivityMoreINavicatecode1001SQ Describe what you are looking forJiminny ... ~# engineering9 24# c-learning-peopie# confusion-cli# curiosity_labFavourites# deal-insights*engineeringi frontend# generalic intra-chance#jiminny-bg• people-with• people-with# platform-te:# platform-tick# product laur# random# releases# sofia-office# support# thank-yous#t the neonle&.- Direct messageR Stefka StoyaAdelina Petro8. Vasil VasilevStoyan Tom% Galya Dimit3 Aneliva AngeS Nikolay Van• Recents• iCloud Drive999 Svnc foldelD DXP4800PLUS-B5F €48 Network• CRM• Orangel• Red• Yellow• Purple|() All Taas.lest> • Jazyky› i #recycleSyncDatav ccreenninearchive.db•data> *2026-04-15• 2026-04-14•2026-04-16• Р 2026.01-17>2026-04-21•2026-04-21archive.db-journalolpesscreenpipe-dav.shscreenpipe.dblest write> Skola) Comnuter Science* ubuntu-24.04.4-live-server-amd64.isc> CODEGitarafastart machine.wavlocation-history.jsonlocation-history 1).son1910229e6394bdc9...2141382106f.ugFV faVsco.js#12011 on JY-20157-AJ-report-not-send-notificationProiectv© TrackAutomatedReportGeneratedEventTest.phg© SendReportNotGeneratedMailJobTest.php•.gitignorec) AutomatedReportsCommand lest.onpAutomateakeporscommana.onge audio.wav© AskJiminnyReportActivityservicelest.pnpAskJiminnykeportActivityservice.ong= custom.logE hubspot-journal-poll.log© Automateareporisservicelest.phgnatedreporisserviceActivitiescountl est.pnp xE laravel.log< phpunit.xml.IS ttt.js= oauth-private.kevC) ReportWithAttachment.phoC) RequestGenerateAskJiminnyReportJobtTest.phgC) RequestGenerateAskJiminnyReportJob.pho(C) SendReportNotGeneratedMailJob.phoC) SendReportMailJob.php= oauth-public.key= storage© SendReportMailobTest.ohd(C) TranscriotionA SummarvReadvEvent.oho= supervisord.pid(C) AutomatedReportGenerated.ohvC) EventServiceProvider.ohvC) CreateDialerActivityEvent.ohowtext-relav..son(C) CreateNudgeCreatedeventonp© CreateActivityLoggedEvent.php© TrackProviderInstalledEvent.phptests• FeatureC UserPilotActivityListener.ongC) UserPilotClient.php(C) PlanhatActivityListener.onpphp api_v2.php• M Intearation(C) AutomatedReportResult.php> Servicesv NUnittestGetActivitiesCountPaylc x 5 CcWTLT:> MActions• M Comnonent<oho> M Confidurationv Mconsoledeclare(strict tvnes=):v D Commands> D ActivitiesD Crm› D Elasticsearchnamesnace Tests Unit Services KloskAutomatedRenorts:, use ..•D Reports©) AutomatedReportsCommand© ImportUsersFromCsvFileTest.ph> C Contractsclacs AutomatedRenontcSenvicpActivitiecCountTest pytends Tecthacpuse TectPrivateMethodi> C Domain>ODTO> @ Enums4 usagesprivate AutomatedReportsService Sservice:> C Events> 0 Exceptions4 usagesprivate Group $group:→tixtures> M Guards37 6t >protected function setUp: void{...}› → Heloers> D Http> Intearations98 D >public function testGetActivitiesCountPayloadWithOne0ffFrequency: void{...}>D Interactionsv Mhlohs> Activity• M AiAutomationi1153 D V1154155156public function testGetActivitiesCountPayloadWithWeeklyFrequencvO: void// Freeze time for predictable date handlingCarbon::setTestNow Carbon:parse( time12025-08-12 18:30:00'))•• M Audiov MAutomatedRenorts158Set un minimal inout pavload with weekly freauencv© CreateResultsTest.phpSinoutPavload = ПC RequestGenerateAck liminnvRer 160'organization' => '6473c918-d8db-4ded-a52b-4febfd7b7c02' ,© RequestGenerateReportJobTestIpenont enahledt => +rue.© SendReportJobTest.php© SendReportMailJobTest.phpiteamsi = |'Acdecola-7h12-40ap-8h7p-366hhh6d42d2111'report_type' => 'exec_summary',(i CondDonortNotConoratodMaille 16I frequencvl =>"wepklv!• MenlandarA2 A VDally - Platorm • in 10111 24 Aol 9:30.10= custom.log X = laravel.logA SF [jiminny@localhost]# hs_local yiminny @localnosy& console [PROD)(C) DatabaseActivities.ong© DealsRepository.php# console [eu)A console [STAGING]There were 8 failures:1) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithWeeklyFrequeFailed asserting that two strings are equal--- Expected[dd dc- '2025-08-05T00:00:00+00:00+ 2025-08-03100:00:00+00:00Jhome/cinclect/ononect/tests/unt/Serynces/Kosk/AutomatedRenonts/AutomatedRenontsSerysceActy1tnescountllest.oho.9l2) Tests \Unit\Services Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayLoadWithMonthLyFrequFailed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11" .home/circleci/oroiect/tests/Unit/Services/Klosk/AutomatedRenorts/AutomatedRenortsServiceActivitiesCounttest.oho:223) Tests\Unit\Services Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequenFailed asserting that two strings are equal,*== Cxoecteu+++ Actualld 00-12022-02-08 00-00-001+*2023-02-05 00:00:00'/home/circleci/proiect/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:1864) Tests Unit\Services Kiosk AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequenFailed asserting that two strings are equal.--- Expected+++ Actualdid dd- 2023-01-15 00:00:00'+'2023-01-01 00:00:00home/circleci/oronect/tests/Unit/Services/Kiosk/AutomatedReoorts/AutomatedRenortsServiceReportGenerationTest.ono: 2055) Tests\Unit|Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlvFreauFailed asserting that two strings are equal,--- Exnectedi+++ Actualc ce-'2022-11-15 00:00:00'412022-10-01 00-00-001Whomo/cindlocinaninat/tostc/Mnit/Soaviicos/Minc/AutomatodPonontc/AutomatodPonantcSoavilcoRonontfononat.ionTost.nhn .2206) Tests\Unit\Services Kiosk AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequeFailed asserting that two strings are equal.--- SynostodlW Windsurf Teams 29:11 UTF-8 Po 4 space:...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75539
|
|
75619
|
2 files committed
JY-20157 fix failing tests
text/ 2 files committed
JY-20157 fix failing tests
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
AutomatedReportsServiceReportGen…tionTest
Run 'AutomatedReportsServiceReportGenerationTest'
Debug 'AutomatedReportsServiceReportGenerationTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
46
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use InvalidArgumentException;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\TeamRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceReportGenerationTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Webhook $webhookService;
private AutomatedReportsRepository $automatedReportsRepository;
private TeamRepository $teamRepository;
protected function setUp(): void
{
parent::setUp();
// Create mocks for dependencies
$this->teamRepository = $this->createMock(TeamRepository::class);
$teamRepository = $this->teamRepository;
$groupRepository = $this->createMock(GroupRepository::class);
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$this->automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$this->webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$this->automatedReportsRepository,
$this->webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
/**
* Test getGenerateReportPayload method with one-off frequency
*/
public function testGetGenerateReportPayloadWithOneOffFrequency(): void
{
$reportUuid = 'report-uuid';
$resultUuid = 'result-uuid';
$callbackUrl = 'https://example.com/webhook/reports/ready';
// Mock report
$report = $this->createMock(AutomatedReport::class);
$report->method('getTeamId')->willReturn(1);
$report->method('getGroups')->willReturn([101, 102]);
$report->method('getType')->willReturn('exec_summary');
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_ONE_OFF);
$report->method('getFrom')->willReturn(Carbon::parse('2023-01-01'));
$report->method('getTo')->willReturn(Carbon::parse('2023-01-31'));
$report->method('getDealAtCallStages')->willReturn([201, 202]);
$report->method('getCurrentDealStages')->willReturn([301, 302]);
$report->method('getDealValueMin')->willReturn(1000);
$report->method('getDealValueMax')->willReturn(5000);
$report->method('getCallTypes')->willReturn(['conference', 'dialer']);
$report->method('getCallDurationMin')->willReturn(600);
$report->method('getCallDurationMax')->willReturn(1800);
$report->method('getAdditionalPromptInput')->willReturn('Focus on sales performance');
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_ONE_OFF);
$report->method('getPlaybookCategories')->willReturn(['category1', 'category2']);
$report->method('getCustomName')->willReturn('My Custom Report');
$result = $this->createMock(AutomatedReportResult::class);
$result->method('getFromDate')->willReturn(Carbon::parse('2023-01-01'));
$result->method('getToDate')->willReturn(Carbon::parse('2023-01-31'));
$result->method('getReport')->willReturn($report);
$this->automatedReportsRepository->method('findResultByUuid')->willReturn($result);
// Set up webhook service to return callback URL
$this->webhookService->expects($this->once())
->method('route')
->with('jiminny.webhook.reports.ready')
->willReturn($callbackUrl);
// Call the method
$result = $this->service->getGenerateReportPayload($report, $resultUuid);
// Verify result
$this->assertEquals(1, $result['team_id']);
$this->assertEquals([101, 102], $result['group_ids']);
$this->assertEquals('exec_summary', $result['report_type']);
$this->assertEquals('2023-01-01T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2023-01-31T23:59:59+00:00', $result['to_date']);
$this->assertEquals([201, 202], $result['call_deal_stage']);
$this->assertEquals([301, 302], $result['current_deal_stage']);
$this->assertEquals(1000, $result['deal_min_value']);
$this->assertEquals(5000, $result['deal_max_value']);
$this->assertEquals(['conference', 'dialer'], $result['call_types']);
$this->assertEquals(600, $result['call_duration_min_seconds']);
$this->assertEquals(1800, $result['call_duration_max_seconds']);
$this->assertEquals('Focus on sales performance', $result['special_requirements']);
$this->assertEquals($resultUuid, $result['request_id']);
$this->assertEquals($callbackUrl, $result['callback_url']);
$this->assertEquals('1 - 31 Jan 2023', $result['report_period']);
$this->assertEquals(['category1', 'category2'], $result['playbook_categories']);
$this->assertEquals('My Custom Report', $result['custom_name']);
}
/**
* Test calculateFromAndToDate method with one-off frequency
*/
public function testCalculateFromAndToDateWithOneOffFrequency(): void
{
// Mock report
$report = $this->createMock(AutomatedReport::class);
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_ONE_OFF);
$report->method('getFrom')->willReturn(Carbon::parse('2023-01-01'));
$report->method('getTo')->willReturn(Carbon::parse('2023-01-31'));
// Access private method using reflection
$result = self::invokePrivateMethod('calculateFromAndToDate', $this->service, [$report]);
// Verify result
$this->assertInstanceOf(Carbon::class, $result['fromDate']);
$this->assertInstanceOf(Carbon::class, $result['toDate']);
$this->assertEquals('2023-01-01', $result['fromDate']->format('Y-m-d'));
$this->assertEquals('2023-01-31', $result['toDate']->format('Y-m-d'));
}
/**
* Test calculateFromAndToDate method with weekly frequency
*/
public function testCalculateFromAndToDateWithWeeklyFrequency(): void
{
// Mock report
$report = $this->createMock(AutomatedReport::class);
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_WEEKLY);
// Freeze time for testing
Carbon::setTestNow(Carbon::parse('2023-02-15 14:30:00'));
// Access private method using reflection
$result = self::invokePrivateMethod('calculateFromAndToDate', $this->service, [$report]);
// Verify result
$this->assertInstanceOf(Carbon::class, $result['fromDate']);
$this->assertInstanceOf(Carbon::class, $result['toDate']);
$this->assertEquals('2023-02-05 00:00:00', $result['fromDate']->format('Y-m-d H:i:s'));
$this->assertEquals('2023-02-11 23:59:59', $result['toDate']->format('Y-m-d H:i:s'));
// Reset frozen time
Carbon::setTestNow();
}
/**
* Test calculateFromAndToDate method with monthly frequency
*/
public function testCalculateFromAndToDateWithMonthlyFrequency(): void
{
// Mock report
$report = $this->createMock(AutomatedReport::class);
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_MONTHLY);
// Freeze time for testing
Carbon::setTestNow(Carbon::parse('2023-02-15 14:30:00'));
// Access private method using reflection
$result = self::invokePrivateMethod('calculateFromAndToDate', $this->service, [$report]);
// Verify result
$this->assertInstanceOf(Carbon::class, $result['fromDate']);
$this->assertInstanceOf(Carbon::class, $result['toDate']);
$this->assertEquals('2023-01-01 00:00:00', $result['fromDate']->format('Y-m-d H:i:s'));
$this->assertEquals('2023-01-31 23:59:59', $result['toDate']->format('Y-m-d H:i:s'));
// Reset frozen time
Carbon::setTestNow();
}
/**
* Test calculateFromAndToDate method with quarterly frequency
*/
public function testCalculateFromAndToDateWithQuarterlyFrequency(): void
{
// Mock report
$report = $this->createMock(AutomatedReport::class);
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_QUARTERLY);
// Freeze time for testing
Carbon::setTestNow(Carbon::parse('2023-02-15 14:30:00'));
// Access private method using reflection
$result = self::invokePrivateMethod('calculateFromAndToDate', $this->service, [$report]);
// Verify result
$this->assertInstanceOf(Carbon::class, $result['fromDate']);
$this->assertInstanceOf(Carbon::class, $result['toDate']);
$this->assertEquals('2022-10-01 00:00:00', $result['fromDate']->format('Y-m-d H:i:s'));
$this->assertEquals('2022-12-31 23:59:59', $result['toDate']->format('Y-m-d H:i:s'));
// Reset frozen time
Carbon::setTestNow();
}
/**
* Test calculateFromAndToDate method with invalid frequency
*/
public function testCalculateFromAndToDateWithInvalidFrequency(): void
{
// Mock report
$report = $this->createMock(AutomatedReport::class);
$report->method('getFrequency')->willReturn('invalid_frequency');
// Expect exception
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Unsupported frequency: invalid_frequency');
// Access private method using reflection
self::invokePrivateMethod('calculateFromAndToDate', $this->service, [$report]);
}
/**
* Test getCallbackUrl method
*/
public function testGetCallbackUrl(): void
{
$callbackUrl = 'https://example.com/webhook/reports/ready';
// Set up webhook service to return callback URL
$this->webhookService->expects($this->once())
->method('route')
->with('jiminny.webhook.reports.ready')
->willReturn($callbackUrl);
// Access private method using reflection
$result = self::invokePrivateMethod('getCallbackUrl', $this->service, []);
// Verify result
$this->assertEquals($callbackUrl, $result);
}
/**
* Test validateDealValues method
*/
public function testValidateDealValues(): void
{
$data = [
'min_deal_value' => 1000,
'max_deal_value' => 5000,
];
$reportData = [];
// Access private method using reflection
$result = self::invokePrivateMethod('validateDealValues', $this->service, [$data, $reportData]);
// Verify result
$this->assertEquals(1000, $result['deal_value_min']);
$this->assertEquals(5000, $result['deal_value_max']);
}
/**
* Test validateDealValues method with min value only
*/
public function testValidateDealValuesWithMinValueOnly(): void
{
$data = [
'min_deal_value' => 1000,
];
$reportData = [];
// Access private method using reflection
$result = self::invokePrivateMethod('validateDealValues', $this->service, [$data, $reportData]);
// Verify result
$this->assertEquals(1000, $result['deal_value_min']);
$this->assertArrayNotHasKey('deal_value_max', $result);
}
/**
* Test validateDealValues method with max value only
*/
public function testValidateDealValuesWithMaxValueOnly(): void
{
$data = [
'max_deal_value' => 5000,
];
$reportData = [];
// Access private method using reflection
$result = self::invokePrivateMethod('validateDealValues', $this->service, [$data, $reportData]);
// Verify result
$this->assertEquals(5000, $result['deal_value_max']);
$this->assertArrayNotHasKey('deal_value_min', $result);
}
/**
* Test validateDealValues method with min value too large
*/
public function testValidateDealValuesWithMinValueTooLarge(): void
{
$data = [
'min_deal_value' => 5000000000, // Larger than 4294967295 (max uint)
];
$reportData = [];
// Expect exception
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Min deal value should be between 0 and 4294967295');
// Access private method using reflection
self::invokePrivateMethod('validateDealValues', $this->service, [$data, $reportData]);
}
/**
* Test validateDealValues method with max value too large
*/
public function testValidateDealValuesWithMaxValueTooLarge(): void
{
$data = [
'max_deal_value' => 5000000000, // Larger than 4294967295 (max uint)
];
$reportData = [];
// Expect exception
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Max deal value should be between 0 and 4294967295');
// Access private method using reflection
self::invokePrivateMethod('validateDealValues', $this->service, [$data, $reportData]);
}
/**
* Test validateDealValues method with min value negative
*/
public function testValidateDealValuesWithMinValueNegative(): void
{
$data = [
'min_deal_value' => -1000,
];
$reportData = [];
// Expect exception
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Min deal value should be between 0 and 4294967295');
// Access private method using reflection
self::invokePrivateMethod('validateDealValues', $this->service, [$data, $reportData]);
}
/**
* Test validateDealValues method with min value greater than max value
*/
public function testValidateDealValuesWithMinValueGreaterThanMaxValue(): void
{
$data = [
'min_deal_value' => 5000,
'max_deal_value' => 1000,
];
$reportData = [];
// Expect exception
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Min deal value cannot be greater than max deal value');
// Access private method using reflection
self::invokePrivateMethod('validateDealValues', $this->service, [$data, $reportData]);
}
/**
* Test validateDateRange method with one-off frequency
*/
public function testValidateDateRangeWithOneOffFrequency(): void
{
// Create a stub for the parseDate method using the TestPrivateMethod trait
$parseDateStub = static function ($dateString) {
if ($dateString === '2023-01-01') {
return '2023-01-01 00:00:00';
}
if ($dateString === '2023-01-31') {
return '2023-01-31 00:00:00';
}
return null;
};
// Set up test data
$data = [
'start_date_period' => '2023-01-01',
'end_date_period' => '2023-01-31',
];
// Manually perform the operations that validateDateRange would do
// This avoids the need to mock private methods
$result = [
'from' => $parseDateStub($data['start_date_period']),
'to' => $parseDateStub($data['end_date_period']),
];
// Verify result
$this->assertEquals('2023-01-01 00:00:00', $result['from']);
$this->assertEquals('2023-01-31 00:00:00', $result['to']);
}
/**
* Test validateDateRange method with one-off frequency and missing dates
*/
public function testValidateDateRangeWithOneOffFrequencyAndMissingDates(): void
{
$data = [];
$reportData = [];
$frequency = 'one_off';
// Expect exception
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Start date and end date are required for one_off frequency');
// Access private method using reflection
self::invokePrivateMethod('validateDateRange', $this->service, [$data, $reportData, $frequency]);
}
/**
* Test validateDateRange method with non-one-off frequency
*/
public function testValidateDateRangeWithNonOneOffFrequency(): void
{
$data = [
'start_date_period' => '2023-01-01',
'end_date_period' => '2023-01-31',
];
$reportData = [];
$frequency = 'weekly';
// Access private method using reflection
$result = self::invokePrivateMethod('validateDateRange', $this->service, [$data, $reportData, $frequency]);
// Verify result
$this->assertNull($result['from']);
$this->assertNull($result['to']);
}
/**
* Test validateCallDurations method
*/
public function testValidateCallDurations(): void
{
$data = [
'min_call_duration' => 10,
'max_call_duration' => 30,
];
$reportData = [];
// Access private method using reflection
$result = self::invokePrivateMethod('validateCallDurations', $this->service, [$data, $reportData]);
// Verify result (minutes converted to seconds)
$this->assertEquals(600, $result['call_duration_min']);
$this->assertEquals(1800, $result['call_duration_max']);
}
/**
* Test validateCallDurations method with min duration only
*/
public function testValidateCallDurationsWithMinDurationOnly(): void
{
$data = [
'min_call_duration' => 10,
];
$reportData = [];
// Access private method using reflection
$result = self::invokePrivateMethod('validateCallDurations', $this->service, [$data, $reportData]);
// Verify result
$this->assertEquals(600, $result['call_duration_min']);
$this->assertArrayNotHasKey('call_duration_max', $result);
}
/**
* Test validateCallDurations method with max duration only
*/
public function testValidateCallDurationsWithMaxDurationOnly(): void
{
$data = [
'max_call_duration' => 30,
];
$reportData = [];
// Access private method using reflection
$result = self::invokePrivateMethod('validateCallDurations', $this->service, [$data, $reportData]);
// Verify result
$this->assertEquals(1800, $result['call_duration_max']);
$this->assertArrayNotHasKey('call_duration_min', $result);
}
/**
* Test validateCallDurations method with min duration too large
*/
public function testValidateCallDurationsWithMinDurationTooLarge(): void
{
$data = [
'min_call_duration' => 100000000, // Too large when converted to seconds
];
$reportData = [];
// Expect exception
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Min call duration should be between 0 and 4294967295');
// Access private method using reflection
self::invokePrivateMethod('validateCallDurations', $this->service, [$data, $reportData]);
}
/**
* Test validateCallTypes method
*/
public function testValidateCallTypes(): void
{
$data = [
'call_type' => ['conference'],
];
$reportData = [];
// Access private method using reflection
$result = self::invokePrivateMethod('validateCallTypes', $this->service, [$data, $reportData]);
// Verify result
$this->assertEquals(['conference'], $result['call_types']);
}
/**
* Test validateCallTypes method with empty call types
*/
public function testValidateCallTypesWithEmptyCallTypes(): void
{
$data = [];
$reportData = [];
// Access private method using reflection
$result = self::invokePrivateMethod('validateCallTypes', $this->service, [$data, $reportData]);
// Verify the result (should default to all call types)
$this->assertEquals(['conference', 'dialer'], $result['call_types']);
}
/**
* Test validateCallTypes method with an invalid call type
*/
public function testValidateCallTypesWithInvalidCallType(): void
{
$data = [
'call_type' => ['invalid_call_type'],
];
$reportData = [];
// Expect exception
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Call type invalid_call_type is invalid');
// Access private method using reflection
self::invokePrivateMethod('validateCallTypes', $this->service, [$data, $reportData]);
}
private function getService($dispatcher): AutomatedReportsService
{
return new AutomatedReportsService(
$this->createMock(TeamRepository::class),
$this->createMock(GroupRepository::class),
$this->createMock(UserRepository::class),
$this->createMock(StageRepository::class),
$this->createMock(DealStagesService::class),
$this->createMock(RecipientsService::class),
$this->createMock(AutomatedReportsRepository::class),
$this->createMock(Webhook::class),
$dispatcher,
$this->createMock(ActivityTypeService::class),
$this->createMock(PlaybookCategoryRepository::class),
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
/**
* Test generateOneOffReport method when status is false
*/
public function testGenerateOneOffReportWithInactiveStatus(): void
{
// Create mock for BusDispatcher
$dispatcher = $this->createMock(BusDispatcher::class);
// Create service instance with mocked dependencies
$service = $this->getService($dispatcher);
// Mock report with inactive status
$report = $this->createMock(AutomatedReport::class);
$report->method('getStatus')->willReturn(false);
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_ONE_OFF);
// Dispatcher should not be called
$dispatcher->expects($this->never())
->method('dispatch');
// Call the private method
self::invokePrivateMethod('generateOneOffReport', $service, [$report]);
}
/**
* Test generateOneOffReport method when frequency is not one-off
*/
public function testGenerateOneOffReportWithNonOneOffFrequency(): void
{
// Create mock for BusDispatcher
$dispatcher = $this->createMock(BusDispatcher::class);
// Create service instance with mocked dependencies
$service = $this->getService($dispatcher);
// Mock report with active status but non-one-off frequency
$report = $this->createMock(AutomatedReport::class);
$report->method('getStatus')->willReturn(true);
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_WEEKLY);
// Dispatcher should not be called
$dispatcher->expects($this->never())
->method('dispatch');
// Call the private method
self::invokePrivateMethod('generateOneOffReport', $service, [$report]);
}
/**
* Test generateOneOffReport method when conditions are met
*/
public function testGenerateOneOffReportWithValidConditions(): void
{
// Create mock for BusDispatcher
$dispatcher = $this->createMock(BusDispatcher::class);
// Create service instance with mocked dependencies
$service = $this->getService($dispatcher);
// Mock report with active status and one-off frequency
$report = $this->createMock(AutomatedReport::class);
$report->method('getStatus')->willReturn(true);
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_ONE_OFF);
$report->method('getUuid')->willReturn('test-uuid');
// Dispatcher should be called once with RequestGenerateReportJob
$dispatcher->expects($this->once())
->method('dispatch')
->with($this->callback(function ($job) {
return $job instanceof \Jiminny\Jobs\AutomatedReports\RequestGenerateReportJob
&& $job->uniqueId() === 'test-uuid';
}));
// Call the private method
self::invokePrivateMethod('generateOneOffReport', $service, [$report]);
}
public function testCreateReportResult(): void
{
$report = $this->createMock(AutomatedReport::class);
$report->method('getId')->willReturn(1);
$expectedResult = $this->createMock(AutomatedReportResult::class);
$this->automatedReportsRepository->expects($this->once())
->method('createResult')
->with([
'report_id' => 1,
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => 'pdf',
])
->willReturn($expectedResult);
$result = $this->service->createReportResult($report, ['media_type' => 'pdf']);
$this->assertSame($expectedResult, $result);
}
public function testFindChildResult(): void
{
$parentResult = $this->createMock(AutomatedReportResult::class);
$childResult = $this->createMock(AutomatedReportResult::class);
$type = AutomatedReportsService::MEDIA_TYPE_PODCAST;
$this->automatedReportsRepository->expects($this->once())
->method('findChildResult')
->with($parentResult, $type)
->willReturn($childResult);
$result = $this->service->findChildResult($parentResult, $type);
$this->assertSame($childResult, $result);
}
public function testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays(): void
{
$resultUuid = 'result-uuid';
$callbackUrl = 'https://example.com/webhook/reports/ready';
Carbon::setTestNow(Carbon::parse('2026-01-20 14:30:00'));
$report = $this->createMock(AutomatedReport::class);
$report->method('getTeamId')->willReturn(1);
$report->method('getGroups')->willReturn([]);
$report->method('getType')->willReturn('exec_summary');
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_WEEKLY);
$report->method('getFrom')->willReturn(null);
$report->method('getTo')->willReturn(null);
$report->method('getDealAtCallStages')->willReturn([]);
$report->method('getCurrentDealStages')->willReturn([]);
$report->method('getDealValueMin')->willReturn(null);
$report->method('getDealValueMax')->willReturn(null);
$report->method('getCallTypes')->willReturn(['conference']);
$report->method('getCallDurationMin')->willReturn(null);
$report->method('getCallDurationMax')->willReturn(null);
$report->method('getAdditionalPromptInput')->willReturn(null);
$report->method('getPlaybookCategories')->willReturn([]);
$report->method('getMediaTypes')->willReturn(['pdf']);
$this->webhookService->expects($this->once())
->method('route')
->with('jiminny.webhook.reports.ready')
->willReturn($callbackUrl);
$result = $this->service->getGenerateReportPayload($report, $resultUuid);
$this->assertEquals('2026-01-11T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2026-01-17T23:59:59+00:00', $result['to_date']);
Carbon::setTestNow();
}
public function testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays(): void
{
$resultUuid = 'result-uuid';
$callbackUrl = 'https://example.com/webhook/reports/ready';
Carbon::setTestNow(Carbon::parse('2026-01-20 05:00:00'));
$report = $this->createMock(AutomatedReport::class);
$report->method('getTeamId')->willReturn(1);
$report->method('getGroups')->willReturn([]);
$report->method('getType')->willReturn('exec_summary');
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_MONTHLY);
$report->method('getFrom')->willReturn(null);
$report->method('getTo')->willReturn(null);
$report->method('getDealAtCallStages')->willReturn([]);
$report->method('getCurrentDealStages')->willReturn([]);
$report->method('getDealValueMin')->willReturn(null);
$report->method('getDealValueMax')->willReturn(null);
$report->method('getCallTypes')->willReturn(['conference']);
$report->method('getCallDurationMin')->willReturn(null);
$report->method('getCallDurationMax')->willReturn(null);
$report->method('getAdditionalPromptInput')->willReturn(null);
$report->method('getPlaybookCategories')->willReturn([]);
$report->method('getMediaTypes')->willReturn(['pdf']);
$this->webhookService->expects($this->once())
->method('route')
->with('jiminny.webhook.reports.ready')
->willReturn($callbackUrl);
$result = $this->service->getGenerateReportPayload($report, $resultUuid);
$this->assertEquals('2025-12-01T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2025-12-31T23:59:59+00:00', $result['to_date']);
Carbon::setTestNow();
}
public function testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays(): void
{
$resultUuid = 'result-uuid';
$callbackUrl = 'https://example.com/webhook/reports/ready';
Carbon::setTestNow(Carbon::parse('2026-01-20 05:00:00'));
$report = $this->createMock(AutomatedReport::class);
$report->method('getTeamId')->willReturn(1);
$report->method('getGroups')->willReturn([]);
$report->method('getType')->willReturn('exec_summary');
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_QUARTERLY);
$report->method('getFrom')->willReturn(null);
$report->method('getTo')->willReturn(null);
$report->method('getDealAtCallStages')->willReturn([]);
$report->method('getCurrentDealStages')->willReturn([]);
$report->method('getDealValueMin')->willReturn(null);
$report->method('getDealValueMax')->willReturn(null);
$report->method('getCallTypes')->willReturn(['conference']);
$report->method('getCallDurationMin')->willReturn(null);
$report->method('getCallDurationMax')->willReturn(null);
$report->method('getAdditionalPromptInput')->willReturn(null);
$report->method('getPlaybookCategories')->willReturn([]);
$report->method('getMediaTypes')->willReturn(['pdf']);
$this->webhookService->expects($this->once())
->method('route')
->with('jiminny.webhook.reports.ready')
->willReturn($callbackUrl);
$result = $this->service->getGenerateReportPayload($report, $resultUuid);
$this->assertEquals('2025-10-01T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2025-12-31T23:59:59+00:00', $result['to_date']);
Carbon::setTestNow();
}
public function testGetGenerateReportPayloadOneOffIncludesCallsOnFinalDay(): void
{
$resultUuid = 'result-uuid';
$callbackUrl = 'https://example.com/webhook/reports/ready';
$report = $this->createMock(AutomatedReport::class);
$report->method('getTeamId')->willReturn(1);
$report->method('getGroups')->willReturn([]);
$report->method('getType')->willReturn('exec_summary');
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_ONE_OFF);
$report->method('getFrom')->willReturn(Carbon::parse('2026-01-01 00:00:00'));
$report->method('getTo')->willReturn(Carbon::parse('2026-01-20 00:00:00'));
$report->method('getDealAtCallStages')->willReturn([]);
$report->method('getCurrentDealStages')->willReturn([]);
$report->method('getDealValueMin')->willReturn(null);
$report->method('getDealValueMax')->willReturn(null);
$report->method('getCallTypes')->willReturn(['conference']);
$report->method('getCallDurationMin')->willReturn(null);
$report->method('getCallDurationMax')->willReturn(null);
$report->method('getAdditionalPromptInput')->willReturn(null);
$report->method('getPlaybookCategories')->willReturn([]);
$report->method('getMediaTypes')->willReturn(['pdf']);
$this->webhookService->expects($this->once())
->method('route')
->with('jiminny.webhook.reports.ready')
->willReturn($callbackUrl);
$result = $this->service->getGenerateReportPayload($report, $resultUuid);
$this->assertEquals('2026-01-01T00:00:00+00:00', $result['from_date']);
$this->assertEquals('2026-01-20T23:59:59+00:00', $result['to_date']);
}
public function testUpdateResultNamesIsCalledWhenCustomNameChanges(): void
{
$reportUuid = 'report-uuid';
$team = $this->createMock(\Jiminny\Models\Team::class);
$team->method('getId')->willReturn(1);
$team->method('hasFeature')->willReturn(true);
$this->teamRepository->method('idOrUuid')->willReturn($team);
$report = $this->createMock(AutomatedReport::class);
$report->method('getCustomName')->willReturn('Old Name');
$report->method('getUuid')->willReturn($reportUuid);
$report->method('getStatus')->willReturn(false);
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_MONTHLY);
$updatedReportTeam = $this->createMock(\Jiminny\Models\Team::class);
$updatedReport = $this->createMock(AutomatedReport::class);
$updatedReport->method('getCustomName')->willReturn('New Name');
$updatedReport->method('getStatus')->willReturn(false);
$updatedReport->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_MONTHLY);
$updatedReport->method('getType')->willReturn('exec_summary');
$updatedReport->method('getTeam')->willReturn($updatedReportTeam);
$updatedReport->method('getUuid')->willReturn('report-uuid');
$updatedReport->method('getFrom')->willReturn(null);
$updatedReport->method('getTo')->willReturn(null);
$updatedReport->method('getDealValueMin')->willReturn(null);
$updatedReport->method('getDealValueMax')->willReturn(null);
$updatedReport->method('getCallTypes')->willReturn([]);
$updatedReport->method('getMediaTypes')->willReturn([AutomatedReportsService::MEDIA_TYPE_PDF]);
$updatedReport->method('getCallDurationMin')->willReturn(null);
$updatedReport->method('getCallDurationMax')->willReturn(null);
$updatedReport->method('getGroups')->willReturn([]);
$updatedReport->method('getDealAtCallStages')->willReturn([]);
$updatedReport->method('getCurrentDealStages')->willReturn([]);
$updatedReport->method('getRecipients')->willReturn(['users' => []]);
$updatedReport->method('getCreator')->willReturn(null);
$updatedReport->method('getAdditionalPromptInput')->willReturn(null);
$updatedReport->method('getCreatedAt')->willReturn(\Illuminate\Support\Carbon::now());
$updatedReport->method('getUpdatedAt')->willReturn(\Illuminate\Support\Carbon::now());
$updatedReport->method('getDeletedAt')->willReturn(null);
$result1 = $this->createMock(AutomatedReportResult::class);
$result1->method('getReport')->willReturn($updatedReport);
$result1->method('getFromDate')->willReturn(Carbon::parse('2025-05-01'));
$result1->method('getToDate')->willReturn(Carbon::parse('2025-05-31'));
$result1->method('getGroups')->willReturn([]);
$result1->method('getMediaType')->willReturn(AutomatedReportsService::MEDIA_TYPE_PDF);
$result1->expects($this->once())
->method('update')
->with(['name' => 'New Name - May 2025']);
$results = new \Illuminate\Database\Eloquent\Collection([$result1]);
$this->automatedReportsRepository->method('findByUuid')->willReturn($report);
$this->automatedReportsRepository->method('update')->willReturn($updatedReport);
$this->automatedReportsRepository->expects($this->once())
->method('getResultsByReport')
->with($updatedReport)
->willReturn($results);
$this->service->update($reportUuid, $this->buildMinimalUpdatePayload());
}
public function testUpdateResultNamesIsNotCalledWhenCustomNameUnchanged(): void
{
$reportUuid = 'report-uuid';
$team = $this->createMock(\Jiminny\Models\Team::class);
$team->method('getId')->willReturn(1);
$team->method('hasFeature')->willReturn(true);
$this->teamRepository->method('idOrUuid')->willReturn($team);
$report = $this->createMock(AutomatedReport::class);
$report->method('getCustomName')->willReturn('Same Name');
$report->method('getUuid')->willReturn($reportUuid);
$report->method('getStatus')->willReturn(false);
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_MONTHLY);
$updatedReportTeam = $this->createMock(\Jiminny\Models\Team::class);
$updatedReport = $this->createMock(AutomatedReport::class);
$updatedReport->method('getCustomName')->willReturn('Same Name');
$updatedReport->method('getStatus')->willReturn(false);
$updatedReport->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_MONTHLY);
$updatedReport->method('getType')->willReturn('exec_summary');
$updatedReport->method('getTeam')->willReturn($updatedReportTeam);
$updatedReport->method('getUuid')->willReturn('report-uuid');
$updatedReport->method('getFrom')->willReturn(null);
$updatedReport->method('getTo')->willReturn(null);
$updatedReport->method('getDealValueMin')->willReturn(null);
$updatedReport->method('getDealValueMax')->willReturn(null);
$updatedReport->method('getCallTypes')->willReturn([]);
$updatedReport->method('getMediaTypes')->willReturn([AutomatedReportsService::MEDIA_TYPE_PDF]);
$updatedReport->method('getCallDurationMin')->willReturn(null);
$updatedReport->method('getCallDurationMax')->willReturn(null);
$updatedReport->method('getGroups')->willReturn([]);
$updatedReport->method('getDealAtCallStages')->willReturn([]);
$updatedReport->method('getCurrentDealStages')->willReturn([]);
$updatedReport->method('getRecipients')->willReturn(['users' => []]);
$updatedReport->method('getCreator')->willReturn(null);
$updatedReport->method('getAdditionalPromptInput')->willReturn(null);
$updatedReport->method('getCreatedAt')->willReturn(\Illuminate\Support\Carbon::now());
$updatedReport->method('getUpdatedAt')->willReturn(\Illuminate\Support\Carbon::now());
$updatedReport->method('getDeletedAt')->willReturn(null);
$this->automatedReportsRepository->method('findByUuid')->willReturn($report);
$this->automatedReportsRepository->method('update')->willReturn($updatedReport);
$this->automatedReportsRepository->expects($this->never())
->method('getResultsByReport');
$this->service->update($reportUuid, $this->buildMinimalUpdatePayload());
}
public function testUpdateResultNamesRollbackToNoCustomNameIncludesTeams(): void
{
$reportUuid = 'report-uuid';
$team = $this->createMock(\Jiminny\Models\Team::class);
$team->method('getId')->willReturn(1);
$team->method('hasFeature')->willReturn(true);
$this->teamRepository->method('idOrUuid')->willReturn($team);
$report = $this->createMock(AutomatedReport::class);
$report->method('getCustomName')->willReturn('Old Custom Name');
$report->method('getUuid')->willReturn($reportUuid);
$report->method('getStatus')->willReturn(false);
$report->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_MONTHLY);
$updatedReportTeam = $this->createMock(\Jiminny\Models\Team::class);
$updatedReport = $this->createMock(AutomatedReport::class);
$updatedReport->method('getCustomName')->willReturn(null);
$updatedReport->method('getStatus')->willReturn(false);
$updatedReport->method('getFrequency')->willReturn(AutomatedReportsService::FREQUENCY_MONTHLY);
$updatedReport->method('getType')->willReturn('exec_summary');
$updatedReport->method('getTeam')->willReturn($updatedReportTeam);
$updatedReport->method('getUuid')->willReturn('report-uuid');
$updatedReport->method('getFrom')->willReturn(null);
$updatedReport->method('getTo')->willReturn(null);
$updatedReport->method('getDealValueMin')->willReturn(null);
$updatedReport->method('getDealValueMax')->willReturn(null);
$updatedReport->method('getCallTypes')->willReturn([]);
$updatedReport->method('getMediaTypes')->willReturn([AutomatedReportsService::MEDIA_TYPE_PDF]);
$updatedReport->method('getCallDurationMin')->willReturn(null);
$updatedReport->method('getCallDurationMax')->willReturn(null);
$updatedReport->method('getGroups')->willReturn([]);
$updatedReport->method('getDealAtCallStages')->willReturn([]);
$updatedReport->method('getCurrentDealStages')->willReturn([]);
$updatedReport->method('getRecipients')->willReturn(['users' => []]);
$updatedReport->method('getCreator')->willReturn(null);
$updatedReport->method('getAdditionalPromptInput')->willReturn(null);
$updatedReport->method('getCreatedAt')->willReturn(\Illuminate\Support\Carbon::now());
$updatedReport->method('getUpdatedAt')->willReturn(\Illuminate\Support\Carbon::now());
$updatedReport->method('getDeletedAt')->willReturn(null);
$result1 = $this->createMock(AutomatedReportResult::class);
$result1->method('getReport')->willReturn($updatedReport);
$result1->method('getFromDate')->willReturn(Carbon::parse('2025-05-01'));
$result1->method('getToDate')->willReturn(Carbon::parse('2025-05-31'));
$result1->method('getGroups')->willReturn([]);
$result1->method('getMediaType')->willReturn(AutomatedReportsService::MEDIA_TYPE_PDF);
$result1->expects($this->once())
->method('update')
->with(['name' => 'Exec Summary - May 2025 - All']);
$results = new \Illuminate\Database\Eloquent\Collection([$result1]);
$this->automatedReportsRepository->method('findByUuid')->willReturn($report);
$this->automatedReportsRepository->method('update')->willReturn($updatedReport);
$this->automatedReportsRepository->expects($this->once())
->method('getResultsByReport')
->with($updatedReport)
->willReturn($results);
$this->service->update($reportUuid, $this->buildMinimalUpdatePayload());
}
private function buildMinimalUpdatePayload(): array
{
return [
'organization' => 'team-uuid',
'report_type' => 'exec_summary',
'report_enabled' => false,
'frequency' => AutomatedReportsService::FREQUENCY_MONTHLY,
'media_types' => [AutomatedReportsService::MEDIA_TYPE_PDF],
];
}
}
Code changed:
Hide
Sync Changes...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75619
|
|
75620
|
2 files committed
JY-20157 fix failing tests
text/ 2 files committed
JY-20157 fix failing tests
text/html
text/html
DMSActivityMorePhpStormViewINavigareJiminny...vXStarredi• jiminny-x-integrati..8 platform-inner-teamE) Channelsic al-chaoter# ai-teamu alerts# backend# c-learning-peoplei confusion-clinic# curiosity labadeal-insichts-dev# engineering# frontend# general# infra-changes# jiminny-bg• people-with-copilo…..8 people-with-zoom-# platform-team# platform-tickets# product_launches# random# releases# soha-office# support# thank-yous# the people of iimi.CodeLaravelKeractorTOOIS• plattorm-inner-...Q 10MessagesChannel OverviewMore1356 |acc*-1120 046 ~89.7 MB~/8MbShow morecomments) jiminny/app Apr 22nd Added by GitHubNikolay Ivanov 3:24 PM•някои нешо ла е настроивал по gitnupachons. Почна ла прави къмити вместо менбез ла сьм му разрешевал.https://github.com/lminnv/app/pull//1200//changes/a0814212108571838241dced451f/5062/be581bИли нешо аз не разбирам?AAe 0 8 20 replies Last reply 18.Nikolay Yankov 3:50 PMreplied to a uhread: някои нещо да е насто..лол, ами предлагам май да му забраним дакьмитва... не знампускам кьм всички ла вилятikolav Yankov 9.38AMШе се забавя за лейлито. Започнете дез мен.Message & platform-inner-team+ AalWindowmelpFV faVsco.js( #12011 on JY-20157-AJ-report-not-send-notification vProiect© TrackAutomatedReportGeneratedEventTest.php© SendReportNotGeneratedMailJobTest.php•.gitignoree audio.wavc) AutomatedReportsCommand lest.onpAutomateakeporscommana.ong= custom.logE hubspot-journal-poll.logE laravel.log< phpunit.xml.I ttt.js= oauth-private.kev© Automatedreporisservicelest.phgnatedreporisserviceActivitiescountl est.onpc ReporiNotgenerated.ohoC) ReportWithAttachment.phoC) RequestGenerateAskJiminnyReportJobTest.phc) RequestGenerateAskJiminnyReportJob.ohp(C) SendReportNotGeneratedMailJob.phoC) SendReportMailJob.php= oauth-public.key= storageC SendReportMailobTest.oho© TranscriptionAiSummaryReadyEvent.php= supervisord.pid(C) AutomatedReportGenerated.ohvC) CreateDialerActivityEvent.ohowtext-relav..son(C) CreateNudgeCreatedeventonp© CreateActivityLoggedEvent.php© TrackProviderInstalledEvent.phptests→ Feature© UserPilotClient.php(C) PlanhatActivityListener.onpphp api_v2.php>M Intearation› Servicesclass Automaredkeporcsservicekeporcbenerac1onlest excenas lescuase46Av NUnit> MActionsM Comnonent142* Test calculateFromAndToDate method with one-off frequency> M Confidurationv Mconsolepublic function testCalculateFromAndToDateWith0ne0ffFrequency: void{...}v D Commands> D ActivitiesD Crm› D ElasticsearchD Reports©) AutomatedReportsCommand© ImportUsersFromCsvFileTest.ph> C Contracts> C Domain>ODTO> @ Enums> C Events> 0 Exceptions162I42* Test calculateFromAndToDate method with weekly frequency165 %public function testCalculateFromAndToDateWithWeeklyFrequencyO: void// Mock reportSreport = Sthis->createMock( originalClassName: AutomatedReport::class):Sreport->method constraint'getrrequency'->w1LLReturn value: AutomatedReportsserv1ce*Freeze time tor testind172carbon::set estrow carbon::porsed uime: 2025-02-15 14158188 90—53→tixtures> M Guards// Access private method using reflectionsresult = self..invokePrivatelethodcmdName: 'calculateFromAndioDate'. Sthis->ge.› → Heloers> D Http> IntearationsVerify resultcarhon: cllass. Sresut"Fromlate"D.>D Interactionsv Mhlohs> Activity• M AiAutomationi182sthis-sassentiinstancelfd exoected• carhon: cllass. Sresut""tolate"n.Sthis-sassentFqualsd exoe2023-02-05 00:00:00' , Sresult['fromDate' ] ->format('VSthis->assentFqualsd exoected'2023-02-11 23:59:59', Sresult['toDate']->format('Y-mn_62— 63• M AudioReset frozen timev MAutomatedRenorts184Canhon• • cotToctlowo•© CreateResultsTest.php© RequestGenerateAskJiminnyRer© RequestGenerateReportJobTest© SendReportJobTest.php@ SendReportMailJobTest.phpi CondDonortNotConoratodMail.la* Test calculateFromAndToDate method with monthly frequencu=68public function testCalculateFromAndToDateWithMonthlyFrequencyO: void{...}Dally - Platorm • In 4m100% LzFri 24 Apr 9:41:35E custom.log x = laravel.logA SF [jiminny@localhost]# hs_local yiminny @localnost« console (PROD)© DealsRepository.php# console [eu)ee ee- '2023-02-08 00:00:00'+'2023-02-05 00:00:00'A console [STAGING]/home/circleci/proiect/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php: 1864) Tests Unit\Services Kiosk AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequenFailed asserting that two strings are equal,•-- Expected+++ Actual- '2023-01-15 00:00:00'+:2023-01-01 00:00:00'home/circleci/oroiect/tests/Unit/Services/Klosk/AutomatedReoorts/AutomatedRenortsServiceRenortGenerationTest.oho: 2055) Tests\Unit\Services Kiosk \AutomatedReports \AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequFailed asserting that two strings are equal,--- Eynectedl+++ Actuald 0д- ' 2022-11-15 00:00:00'412029.19-01 00-00-901/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php: 2366) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeekLyFrequeralled asserting that two strinas are equau--- Expected+++ Actualee ee- 2026-01-13700:00:00+00:00'+ 2026-01-11700:00:00+00:00'home/circleci/oronect/tests/Unit/Services/Kiosk/AutomatedReoorts/AutomatedRenortsServiceReportGenerationTest.ono.77:)Tests \Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayLoadWithMonthlyFrecFalled asserting that twostrinas are equal--- Expected*** Actuallc ce-12025-12-20700-00:00÷00•001+12925-12-A1700•00:00+00:00'Whomo/cindloci/naninat/tostc/Mnit/Soaviicos/MinckAutomatodRonontc/AutomatadPonantcSoawiicoRonontfonenat.ionTost_nhn..81l8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTe# 2 files committedFailed asserting that two strings are egual.JY-2015/ Tix Tallina tests--- ExpectedEdit Commit Messaae.WN Windsurf Toams 12-6UTF.8io 4 spaces...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75620
|
|
75623
|
2 files committed
JY-20157 fix failing tests
text/ 2 files committed
JY-20157 fix failing tests
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
AutomatedReportsServiceReportGen…tionTest
Run 'AutomatedReportsServiceReportGenerationTest'
Debug 'AutomatedReportsServiceReportGenerationTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
46
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use InvalidArgumentException;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\TeamRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceReportGenerationTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Webhook $webhookService;
private AutomatedReportsRepository $automatedReportsRepository;
private TeamRepository $teamRepository;
protected function setUp(): void
{
parent::setUp();
// Create mocks for dependencies
$this->teamRepository = $this->createMock(TeamRepository::class);
$teamRepository = $this->teamRepository;
$groupRepository = $this->createMock(GroupRepository::class);
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$this->automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$this->webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$this->automatedReportsRepository,
$this->webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
/**
* Test getGenerateReportPayload method with one-off frequency
*/
public function testGetGenerateReportPayloadWithOneOffFrequency(): void
{
$reportUuid = 'report-uuid';
$resultUuid = 'result-uuid';
$callbackUrl = '[URL_WITH_CREDENTIALS] @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75623
|
|
75624
|
2 files committed
JY-20157 fix failing tests
text/ 2 files committed
JY-20157 fix failing tests
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
AutomatedReportsServiceReportGen…tionTest
Run 'AutomatedReportsServiceReportGenerationTest'
Debug 'AutomatedReportsServiceReportGenerationTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
46
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use InvalidArgumentException;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\TeamRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceReportGenerationTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Webhook $webhookService;
private AutomatedReportsRepository $automatedReportsRepository;
private TeamRepository $teamRepository;
protected function setUp(): void
{
parent::setUp();
// Create mocks for dependencies
$this->teamRepository = $this->createMock(TeamRepository::class);
$teamRepository = $this->teamRepository;
$groupRepository = $this->createMock(GroupRepository::class);
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$this->automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$this->webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$this->automatedReportsRepository,
$this->webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
/**
* Test getGenerateReportPayload method with one-off frequency
*/
public function testGetGenerateReportPayloadWithOneOffFrequency(): void
{
$reportUuid = 'report-uuid';
$resultUuid = 'result-uuid';
$callbackUrl = '[URL_WITH_CREDENTIALS] @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75624
|
|
75637
|
2 files committed
JY-20157 fix failing tests
text/ 2 files committed
JY-20157 fix failing tests
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
AutomatedReportsServiceReportGen…tionTest
Run 'AutomatedReportsServiceReportGenerationTest'
Debug 'AutomatedReportsServiceReportGenerationTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
46
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use InvalidArgumentException;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\TeamRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceReportGenerationTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Webhook $webhookService;
private AutomatedReportsRepository $automatedReportsRepository;
private TeamRepository $teamRepository;
protected function setUp(): void
{
parent::setUp();
// Create mocks for dependencies
$this->teamRepository = $this->createMock(TeamRepository::class);
$teamRepository = $this->teamRepository;
$groupRepository = $this->createMock(GroupRepository::class);
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$this->automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$this->webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$this->automatedReportsRepository,
$this->webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
/**
* Test getGenerateReportPayload method with one-off frequency
*/
public function testGetGenerateReportPayloadWithOneOffFrequency(): void
{
$reportUuid = 'report-uuid';
$resultUuid = 'result-uuid';
$callbackUrl = '[URL_WITH_CREDENTIALS] @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75637
|
|
75638
|
2 files committed
JY-20157 fix failing tests
text/ 2 files committed
JY-20157 fix failing tests
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
AutomatedReportsServiceReportGen…tionTest
Run 'AutomatedReportsServiceReportGenerationTest'
Debug 'AutomatedReportsServiceReportGenerationTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Code changed:
Hide
Sync Changes
Hide This Notification
46
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use Illuminate\Contracts\Bus\Dispatcher as BusDispatcher;
use Illuminate\Support\Carbon;
use InvalidArgumentException;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Repositories\TeamRepository;
use Jiminny\Repositories\UserRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\AskAnythingRepository;
use Tests\TestCase;
use Tests\Unit\Traits\TestPrivateMethod;
class AutomatedReportsServiceReportGenerationTest extends TestCase
{
use TestPrivateMethod;
private AutomatedReportsService $service;
private Webhook $webhookService;
private AutomatedReportsRepository $automatedReportsRepository;
private TeamRepository $teamRepository;
protected function setUp(): void
{
parent::setUp();
// Create mocks for dependencies
$this->teamRepository = $this->createMock(TeamRepository::class);
$teamRepository = $this->teamRepository;
$groupRepository = $this->createMock(GroupRepository::class);
$userRepository = $this->createMock(UserRepository::class);
$stageRepository = $this->createMock(StageRepository::class);
$dealStagesService = $this->createMock(DealStagesService::class);
$recipientsService = $this->createMock(RecipientsService::class);
$this->automatedReportsRepository = $this->createMock(AutomatedReportsRepository::class);
$this->webhookService = $this->createMock(Webhook::class);
$dispatcher = $this->createMock(BusDispatcher::class);
$activityTypeService = $this->createMock(ActivityTypeService::class);
$playbookCategoryRepository = $this->createMock(PlaybookCategoryRepository::class);
// Create service instance with mocked dependencies
$this->service = new AutomatedReportsService(
$teamRepository,
$groupRepository,
$userRepository,
$stageRepository,
$dealStagesService,
$recipientsService,
$this->automatedReportsRepository,
$this->webhookService,
$dispatcher,
$activityTypeService,
$playbookCategoryRepository,
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
/**
* Test getGenerateReportPayload method with one-off frequency
*/
public function testGetGenerateReportPayloadWithOneOffFrequency(): void
{
$reportUuid = 'report-uuid';
$resultUuid = 'result-uuid';
$callbackUrl = '[URL_WITH_CREDENTIALS] @@
-'2025-08-05T00:00:00+00:00'
+'2025-08-03T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:194
2) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceActivitiesCountTest::testGetActivitiesCountPayloadWithMonthlyFrequency
Failed asserting that '2025-07-31T23:59:59+00:00' starts with "2025-08-11".
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceActivitiesCountTest.php:226
3) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithWeeklyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-02-08 00:00:00'
+'2023-02-05 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:180
4) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithMonthlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2023-01-15 00:00:00'
+'2023-01-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:205
5) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testCalculateFromAndToDateWithQuarterlyFrequency
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2022-11-15 00:00:00'
+'2022-10-01 00:00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:230
6) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithWeeklyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2026-01-13T00:00:00+00:00'
+'2026-01-11T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:778
7) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithMonthlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-12-20T00:00:00+00:00'
+'2025-12-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:816
8) Tests\Unit\Services\Kiosk\AutomatedReports\AutomatedReportsServiceReportGenerationTest::testGetGenerateReportPayloadWithQuarterlyFrequencyUsesFullDays
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'2025-10-20T00:00:00+00:00'
+'2025-10-01T00:00:00+00:00'
/home/circleci/project/tests/Unit/Services/Kiosk/AutomatedReports/AutomatedReportsServiceReportGenerationTest.php:854
FAILURES!
Tests: 10847, Assertions: 46370, Failures: 8, Warnings: 24, Deprecations: 80, PHPUnit Deprecations: 434, Skipped: 51, Incomplete: 65.
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75638
|
|
75761
|
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
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75761
|
|
75762
|
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
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
75762
|
|
76146
|
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
Show Replace Field
Search History
\Log
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
1/1
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Sync Changes
Hide This Notification
Code changed:
Hide
1
1
1
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;
}
}
Code changed:...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76146
|
|
76147
|
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
Show Replace Field
Search History
DMSActivityMorerireroxToolsHelpcalMistorbookmarksJiminny …..vXStarredi• jiminny-x-integrati..8 platform-inner-teamE) Channels# ai-chapter# ai-team# alerts# backend# c-learning-peoplei confusion-clinic# curiosity_labadeal-insichts-dev# engineering# frontend# general# infra-changes# jiminny-bg8 people-with-copilo...8 people-with-zoom-# platform-team# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the Deople of iimi...ProtllesWindow& platform-inner-...& 10MessagesChannel OverviewMoreYesterdayjinnylaop Aor 22nd Added by GitHubNikolay Ivanov 3:24 PMнякой нещо да е настроивал по githubactions. Почна да прави къмити вместо менбез да съм му разрешевал?https:/github.com/lminnv/app/pull/1200//changes/a68f42f210859f838a4fdced451f750627besoioИли нещо аз не разбирам?0AA0e 20 replies Last reply 18...Nikolay Yankov 3:50PMreplied to a uhread: някои нешо ла е насто..лол. ами предлагам маи ла му заораним лапускам към всички ла вилятNikolav Yankov 9.38 AMЩе се забавя за дейлито. Започнете без Мен.Aneliva Angelova 9:43 AMIДобро утро, няма да успея да вляза влейлито. Пествам ньлжовете.Message & platform-inner-team+ Aa I..•) New TabAl reports promotion pages by nik• JY-9712 | Nuges to expire after on8 Jiminnyu Userpilot Logged-activityJY-20157 add not enough activ XPipelines - jiminny/app+ New Tab©github.com/jimjiminny / app 8<> Code87 Pull requests 31( Agents |© Actions•• Wiki © Security and quality 32 ~ Insights 3 Settings@ On April 24 we'll start using GitHub Copilot interaction data for Al model training unless you opt out. Review this update and manage your preferences in your GitHub account settings.JY-20157 add not enough activities notification #12011 •$1 Open LakyLak wants to merge 2 commits into master from JY-20157-AJ-report-not-send-notification@) Conversation o• Commits 2|- Checks 21E Files changed 13A © All commits +Q Filter files...apo/Console/Commands/Reports/AutomatedReportsCommand.ohp@ -61,21 +61,29 @ public function handle(): intv = Console/Commands/Renorts|Snow = Carbon: : now();E AutomatedReportsCommand...v Jobs/AutomatedReportsE RequestGenerateAskJiminnyR...SendReportNotGeneratedMail...v @ Listeners/AutomatedReports/U….E TrackAutomatedReportGener...v # Mail/ReportsS1sMondav = Snow->1SMonday)S1sr1rstDayUtMonth = Snow->day === 1;ScurrentMonth = Snow->month.// Check if the current month is a quarterly month (January, April, July, October)$isQuarterlyMonth = in_array($currentMonth, [1, 4, 7, 10], true);$this->logger->info(self::LOG_PREFIX . ' Checking conditions', [+ ReportNotGenerated.ohp |"1SMonday' => S1SMonday,~ E Services/Kiosk/AutomatedRepo…..AskJiminnyReportActivityServ…'isFirstDay0fMonth' => SisFirstDay0fMonth,'currentMonth' => ScurrentMonth.E AutomatedReportsService.php'isQuarterlyMonth' => SisQuarterlyMonth,~E resources/views/emails/reportsreport-not-generated.blade.php/I Process dailv revortsl• F tests/UnitSthis->processReports(AutomatedReportsService::FREQUENCYDAILY):~ Jobs/AutomatedReportsE ReguestGenerateAsk JiminnvR....v = listeners/AutomatedRenorts/U.₴ TrackAutomatedReportGener..v E Services/Kiosk/AutomatedRepo…..E AskJiminnyReportActivityServ....AutomatedReportsServiceActi…./ Process weekly renorts on Mondavcif (SisMondav) {64 +67 +74 +86 +@40@ Daily - Platform - nowQ Type to search100% C4 8• Fri 24 Apr 9:46:13• Checks pending Code • (Preview) -+384 -52 9000C 0 I 13 viewedSubmit review+10 -2 mane [ Viewed0 ...Snow = Carhon:.nowdSisMondav = Snow->1SMonday)"Sisweekend = $now->isWeekend():SisFirstDay0fMonth = Snow->day === 1;ScurrentMonth = Snow->month.SisManualTrigger = $this->option('report-id') !== null;// Check if the current month is a quarterly month (January, April, July, October)$isQuarterlyMonth = in_array($currentMonth, [1, 4, 7, 10], true);Sthis->loager->info(self::L0G PREFIX . ' Checkina conditions'. [I"isMonday' => SisMonday,'isweekend' => $isWeekend,'isFirstDay0fMonth' => $isFirstDay0fMonth,'currentMonth' => ScurrentMonth.l'isQuarterlyMonth' => SisQuarterlyMonth,/ Process dailv renorts on weekdavs onlv (skio Saturdav/Sundav)...// Manual triggers via --report-id bypass the weekend skip.if (I Sisweekend || SisManualTriager) {Sthis->processReports(AutomatedReportsService::FREQUENCY_DAILY):} else {ISthis->logger->info(self::L0G PREFIX . ' Skipping daily reports on weekend'):/ Process weekly renorts on Mondavslif (SisMonday) {...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76147
|
|
76661
|
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
13
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers\API\UserAutomatedReports;
use Illuminate\Support\Carbon;
use Illuminate\Http\JsonResponse;
use Jiminny\Exceptions\ApplicationException;
use Jiminny\Http\Controllers\Controller;
use Jiminny\Http\Responses\Api\Response;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\ApiResponseService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\ReportSort;
use Jiminny\Services\Kiosk\AutomatedReports\ReportSortDirection;
use Illuminate\Http\Request;
use Throwable;
class UserAutomatedReportsController extends Controller
{
public const int RESULTS_PER_PAGE = 25;
public const string SORT_COLUMN = 'sort_column';
public const string SORT_DIRECTION = 'sort_direction';
public function __construct(
private readonly AutomatedReportsRepository $automatedReportsRepository,
private readonly AutomatedReportsService $automatedReportsService,
private readonly ApiResponseService $apiResponseService,
private readonly Response $response
) {
parent::__construct();
}
/**
* @throws ApplicationException
*/
public function list(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
$teamIds = $request->has('team')
? (array) $request->get('team')
: [];
$reportTypes = $request->has('report_type')
? (array) $request->get('report_type')
: [];
$name = $request->has('name') ? trim($request->get('name', '')) : null;
try {
$fromDate = $request->has('from_date') ? Carbon::parse($request->get('from_date')) : null;
$toDate = $request->has('to_date') ? Carbon::parse($request->get('to_date')) : null;
} catch (\Exception) {
return $this->response->errorWrongArgs('Invalid date.');
}
$page = $request->has('page') ? (int) $request->get('page') : 1;
$sort = ReportSort::tryFrom(
$request->get(self::SORT_COLUMN, '')
) ?? ReportSort::GENERATED_AT;
$sortDirection = ReportSortDirection::tryFrom(
strtolower($request->get(self::SORT_DIRECTION, ''))
) ?? ReportSortDirection::DESC;
$paginatedUserReports = $this->automatedReportsRepository->getPaginatedUserReports(
user: $user,
sort: $sort,
sortDirection: $sortDirection,
resultsPerPage: self::RESULTS_PER_PAGE,
page: $page,
fromDate: $fromDate,
toDate: $toDate,
teamIds: array_map('intval', $teamIds),
reportTypes: $reportTypes,
name: $name,
);
$reportResults = $this->automatedReportsService->transformReportResults(
$paginatedUserReports->getCollection()
);
$team = $user->getTeam();
$reportTypeFilter = $this->automatedReportsService->getReportTypeFieldData(
shortVersion: true,
team: $team
);
$data = $this->apiResponseService->fromPaginatorToArray(
paginator: $paginatedUserReports,
data: $reportResults,
moreMeta: [
self::SORT_COLUMN => $sort->value,
self::SORT_DIRECTION => $sortDirection->value,
],
filters: [
$reportTypeFilter['id'] => $reportTypeFilter,
],
);
return $this->response->withArray($data);
}
public function delete(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$result = $this->automatedReportsRepository->findResultByUuidForUser($uuid, $user);
if ($result === null) {
return new JsonResponse(
data: ['error' => 'Report not found'],
status: JsonResponse::HTTP_NOT_FOUND
);
}
$result->delete();
return new JsonResponse(null, JsonResponse::HTTP_NO_CONTENT);
} catch (Throwable $e) {
return new JsonResponse(
data: ['error' => 'Failed to delete report result'],
status: JsonResponse::HTTP_INTERNAL_SERVER_ERROR
);
}
}
}
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...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76661
|
|
76662
|
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
DMSActivityMorerireroxToolsHelpcalMistorbookmarksJiminny …..vXStarredi• jiminny-x-integrati..8 platform-inner-teamE) Channels# ai-chapter# ai-team# alerts# backend# c-learning-peoplei confusion-clinic# curiosity_labadeal-insichts-dev# engineering# frontend# general# infra-changes# jiminny-bg8 people-with-copilo...8 people-with-zoom-# platform-team# platform-tickets# product_launches# random# releases# sofia-office# support# thank-yous# the Deople of iimi...ProtllesWindow& platform-inner-...& 10MessagesChannel OverviewMoreYesterdayjinnylaop Aor 22nd Added by GitHubNikolay Ivanov 3:24 PMнякой нещо да е настроивал по githubactions. Почна да прави къмити вместо менбез да съм му разрешевал?https:/github.com/lminnv/app/pull/1200//changes/a68f42f210859f838a4fdced451f750627besoioИли нещо аз не разбирам?0AA0e 20 replies Last reply 18...Nikolay Yankov 3:50PMreplied to a uhread: някои нешо ла е насто..лол. ами предлагам маи ла му заораним лапускам към всички ла вилятNikolav Yankov 9.38 AMЩе се забавя за дейлито. Започнете без Мен.Aneliva Angelova 9:43 AMIДобро утро, няма да успея да вляза влейлито. Пествам ньлжовете.Message & platform-inner-team+ Aa I..•) New TabAl reports promotion pages by nik• JY-9712 | Nuges to expire after on8 Jiminnyu Userpilot Logged-activityJY-20157 add not enough activ XPipelines - jiminny/app+ New Tab©github.com/jimjiminny / app 8<> Code87 Pull requests 31( Agents |© Actions•• Wiki © Security and quality 32 ~ Insights 3 Settings@ On April 24 we'll start using GitHub Copilot interaction data for Al model training unless you opt out. Review this update and manage your preferences in your GitHub account settings.JY-20157 add not enough activities notification #12011 •$1 Open LakyLak wants to merge 2 commits into master from JY-20157-AJ-report-not-send-notification@) Conversation o• Commits 2|- Checks 21E Files changed 13A © All commits +Q Filter files...apo/Console/Commands/Reports/AutomatedReportsCommand.ohp@ -61,21 +61,29 @ public function handle(): intv = Console/Commands/Renorts|Snow = Carbon: : now();E AutomatedReportsCommand...v Jobs/AutomatedReportsE RequestGenerateAskJiminnyR...SendReportNotGeneratedMail...v @ Listeners/AutomatedReports/U….E TrackAutomatedReportGener...v # Mail/ReportsS1sMondav = Snow->1SMonday)S1sr1rstDayUtMonth = Snow->day === 1;ScurrentMonth = Snow->month.// Check if the current month is a quarterly month (January, April, July, October)$isQuarterlyMonth = in_array($currentMonth, [1, 4, 7, 10], true);$this->logger->info(self::LOG_PREFIX . ' Checking conditions', [+ ReportNotGenerated.ohp |"1SMonday' => S1SMonday,~ E Services/Kiosk/AutomatedRepo…..AskJiminnyReportActivityServ…'isFirstDay0fMonth' => SisFirstDay0fMonth,'currentMonth' => ScurrentMonth.E AutomatedReportsService.php'isQuarterlyMonth' => SisQuarterlyMonth,~E resources/views/emails/reportsreport-not-generated.blade.php/I Process dailv revortsl• F tests/UnitSthis->processReports(AutomatedReportsService::FREQUENCYDAILY):~ Jobs/AutomatedReportsE ReguestGenerateAsk JiminnvR....v = listeners/AutomatedRenorts/U.₴ TrackAutomatedReportGener..v E Services/Kiosk/AutomatedRepo…..E AskJiminnyReportActivityServ....AutomatedReportsServiceActi…./ Process weekly renorts on Mondavcif (SisMondav) {64 +67 +74 +86 +@40@ Daily - Platform - nowQ Type to search100% C4 8• Fri 24 Apr 9:46:13• Checks pending Code • (Preview) -+384 -52 9000C 0 I 13 viewedSubmit review+10 -2 mane [ Viewed0 ...Snow = Carhon:.nowdSisMondav = Snow->1SMonday)"Sisweekend = $now->isWeekend():SisFirstDay0fMonth = Snow->day === 1;ScurrentMonth = Snow->month.SisManualTrigger = $this->option('report-id') !== null;// Check if the current month is a quarterly month (January, April, July, October)$isQuarterlyMonth = in_array($currentMonth, [1, 4, 7, 10], true);Sthis->loager->info(self::L0G PREFIX . ' Checkina conditions'. [I"isMonday' => SisMonday,'isweekend' => $isWeekend,'isFirstDay0fMonth' => $isFirstDay0fMonth,'currentMonth' => ScurrentMonth.l'isQuarterlyMonth' => SisQuarterlyMonth,/ Process dailv renorts on weekdavs onlv (skio Saturdav/Sundav)...// Manual triggers via --report-id bypass the weekend skip.if (I Sisweekend || SisManualTriager) {Sthis->processReports(AutomatedReportsService::FREQUENCY_DAILY):} else {ISthis->logger->info(self::L0G PREFIX . ' Skipping daily reports on weekend'):/ Process weekly renorts on Mondavslif (SisMonday) {...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76662
|
|
76663
|
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
13
1
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Http\Controllers\API\UserAutomatedReports;
use Illuminate\Support\Carbon;
use Illuminate\Http\JsonResponse;
use Jiminny\Exceptions\ApplicationException;
use Jiminny\Http\Controllers\Controller;
use Jiminny\Http\Responses\Api\Response;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\ApiResponseService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\ReportSort;
use Jiminny\Services\Kiosk\AutomatedReports\ReportSortDirection;
use Illuminate\Http\Request;
use Throwable;
class UserAutomatedReportsController extends Controller
{
public const int RESULTS_PER_PAGE = 25;
public const string SORT_COLUMN = 'sort_column';
public const string SORT_DIRECTION = 'sort_direction';
public function __construct(
private readonly AutomatedReportsRepository $automatedReportsRepository,
private readonly AutomatedReportsService $automatedReportsService,
private readonly ApiResponseService $apiResponseService,
private readonly Response $response
) {
parent::__construct();
}
/**
* @throws ApplicationException
*/
public function list(Request $request): JsonResponse
{
/** @var User $user */
$user = $request->user();
$teamIds = $request->has('team')
? (array) $request->get('team')
: [];
$reportTypes = $request->has('report_type')
? (array) $request->get('report_type')
: [];
$name = $request->has('name') ? trim($request->get('name', '')) : null;
try {
$fromDate = $request->has('from_date') ? Carbon::parse($request->get('from_date')) : null;
$toDate = $request->has('to_date') ? Carbon::parse($request->get('to_date')) : null;
} catch (\Exception) {
return $this->response->errorWrongArgs('Invalid date.');
}
$page = $request->has('page') ? (int) $request->get('page') : 1;
$sort = ReportSort::tryFrom(
$request->get(self::SORT_COLUMN, '')
) ?? ReportSort::GENERATED_AT;
$sortDirection = ReportSortDirection::tryFrom(
strtolower($request->get(self::SORT_DIRECTION, ''))
) ?? ReportSortDirection::DESC;
$paginatedUserReports = $this->automatedReportsRepository->getPaginatedUserReports(
user: $user,
sort: $sort,
sortDirection: $sortDirection,
resultsPerPage: self::RESULTS_PER_PAGE,
page: $page,
fromDate: $fromDate,
toDate: $toDate,
teamIds: array_map('intval', $teamIds),
reportTypes: $reportTypes,
name: $name,
);
$reportResults = $this->automatedReportsService->transformReportResults(
$paginatedUserReports->getCollection()
);
$team = $user->getTeam();
$reportTypeFilter = $this->automatedReportsService->getReportTypeFieldData(
shortVersion: true,
team: $team
);
$data = $this->apiResponseService->fromPaginatorToArray(
paginator: $paginatedUserReports,
data: $reportResults,
moreMeta: [
self::SORT_COLUMN => $sort->value,
self::SORT_DIRECTION => $sortDirection->value,
],
filters: [
$reportTypeFilter['id'] => $reportTypeFilter,
],
);
return $this->response->withArray($data);
}
public function delete(Request $request, string $uuid): JsonResponse
{
/** @var User $user */
$user = $request->user();
try {
$result = $this->automatedReportsRepository->findResultByUuidForUser($uuid, $user);
if ($result === null) {
return new JsonResponse(
data: ['error' => 'Report not found'],
status: JsonResponse::HTTP_NOT_FOUND
);
}
$result->delete();
return new JsonResponse(null, JsonResponse::HTTP_NO_CONTENT);
} catch (Throwable $e) {
return new JsonResponse(
data: ['error' => 'Failed to delete report result'],
status: JsonResponse::HTTP_INTERNAL_SERVER_ERROR
);
}
}
}
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...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76663
|
|
76668
|
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
1
2
1
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;
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);
try {
foreach ($this->resolveUsers($automatedReport) as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
$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;
}
}
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
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76668
|
|
76669
|
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
1
2
1
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;
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);
try {
foreach ($this->resolveUsers($automatedReport) as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
$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;
}
}
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...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76669
|
|
76670
|
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
1
2
1
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;
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);
try {
foreach ($this->resolveUsers($automatedReport) as $user) {
$this->userPilotClient->track($user, $eventName, $payload);
}
} catch (GuzzleException $e) {
$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;
}
}
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...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76670
|
|
76778
|
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...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76778
|
|
76779
|
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
LanguageDetection, folder
LiveFeed, folder
Locks, folder
Math, folder
MediaPipeline, folder
MeetingBot, folder
MobileSettings, folder
Model, folder
Notification, folder
Nudge, folder...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76779
|
|
76780
|
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
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...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76780
|
|
76781
|
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
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
MatchCrmObjectsCommand.php, class
MatchOpportunityActivitiesCommand.php, class
MigrateProvider.php, class
ProcessHubspotObjectsSyncBatches.php, class
PurgeDeletedOpportunitiesCommand.php, class
ResetGovernorLimits.php, class
SendNotLogged.php, class
SetupActivityTypeForFollowUp.php, final class
SetupCloseCrm.php, class
SetupCopperCrm.php, class
SetupCrmCommand.php, abstract class
SetupLayouts.php, class
SyncAccount.php, class
SyncContact.php, class
SyncFieldMetadata.php, class...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
76781
|
|
77260
|
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
Sync Changes
Hide This Notification
Code changed:
Hide
3
115
4
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
Editor for custom.log
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
77260
|
|
77263
|
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
Sync Changes
Hide This Notification
Code changed:
Hide
3
115
4
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Console\Commands;
use Carbon\Carbon;
use Illuminate\Console\Command;
use InvalidArgumentException;
use Jiminny\Jobs\AutomatedReports\RequestGenerateAskJiminnyReportJob;
use Jiminny\Jobs\AutomatedReports\SendReportMailJob;
use Jiminny\Jobs\JobDispatcherInterface;
use Jiminny\Models\Activity;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Services\Activity\CrmOwnerResolver;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\UserPilot\UserPilotClient;
/**
* Class JiminnyDebugCommand
*
* @package Jiminny\Console\Commands
*/
class JiminnyDebugCommand extends Command
{
public const string FREQUENCY_DAILY = 'daily';
public const string FREQUENCY_WEEKLY = 'weekly';
public const string FREQUENCY_MONTHLY = 'monthly';
public const string FREQUENCY_QUARTERLY = 'quarterly';
public const string FREQUENCY_ONE_OFF = 'one_off';
protected $signature = 'jiminny:debug';
public function handle(
JobDispatcherInterface $jobDispatcher,
AutomatedReportsService $automatedReportsService,
AutomatedReportsRepository $automatedReportsRepository,
UserPilotClient $userPilotClient
): void {
$user = User::find(143);
// $count = $automatedReportsRepository->countUserReports($user);
// $this->info("Count: {$count}");
// $count = $automatedReportsRepository->countAllUserReports($user);
// $this->info("All count: {$count}");
$payload = [
'report_type' => 'ask_jiminny',
'frequency' => 'weekly',
];
$userPilotClient->track($user, 'ask-jiminny-report-generated', $payload);
exit(1);
$now = Carbon::now()->subDay(1);
$this->info("Now: {$now->toDateTimeString()}");
$weekStart = Carbon::getWeekStartsAt();
$this->info("Now: {$weekStart}");
// $from = $now->copy()->previousWeekday()->startOfDay();
// $to = $now->copy()->previousWeekday()->endOfDay();
// $fromOld = $now->copy()->subWeeks(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subWeek()->startOfWeek();
// $toNew = $now->copy()->subWeek()->endOfWeek();
// $fromOld = $now->copy()->subMonths(1)->startOfDay();
// $toOld = $now->copy()->subDay()->endOfDay();
// $fromNew = $now->copy()->subMonthNoOverflow()->startOfMonth();
// $toNew = $now->copy()->subMonthNoOverflow()->endOfMonth();
$fromOld = $now->copy()->subMonths(3)->startOfDay();
$toOld = $now->copy()->subDay()->endOfDay();
$fromNew = $now->copy()->subQuarterNoOverflow()->startOfQuarter();
$toNew = $now->copy()->subQuarterNoOverflow()->endOfQuarter();
$this->info("From old: {$fromOld->toDateTimeString()}");
$this->info("To old: {$toOld->toDateTimeString()}");
$this->info("From new: {$fromNew->toDateTimeString()}");
$this->info("To new: {$toNew->toDateTimeString()}");
exit(1);
$report = AutomatedReport::find(71);
$job = new RequestGenerateAskJiminnyReportJob($report->getUuid());
$jobDispatcher->dispatch($job);
exit(1);
// $this->formatDate($jobDispatcher);
// $this->sendMail($jobDispatcher, $automatedReportsService);
// $this->crmService();
$this->getPayload($automatedReportsService);
exit(1);
}
private function crmService()
{
$activity = Activity::find(418141);
$team = Team::find(19);
$config = $team->getCrmConfiguration();
$crmResolver = app(CrmOwnerResolver::class, [
'team' => $team,
'integrationAdmin' => $team->getOwner(),
'providerSlug' => $config->getProviderName(),
]);
$crmService = $crmResolver->prepareCrmService();
$crmService->createTranscriptNotes($activity);
}
private function sendMail(JobDispatcherInterface $jobDispatcher, AutomatedReportsService $automatedReportsService)
{
$reportUuid = '';
// $report = $automatedReportsService->getReportResult($reportUuid);
$report = AutomatedReportResult::find(275);
$validRecipients = $automatedReportsService->getValidRecipientUsers(
$report->getReport(),
includeJiminny: true,
);
$recipient = $validRecipients[0];
$fileName = $automatedReportsService->getReportFileName($report);
$typeName = $report->getReport()->getCustomName()
?? $automatedReportsService->getReportTypeName($report);
$teamsName = $automatedReportsService->getReportTeamsName($report);
$periodName = $automatedReportsService->getReportPeriodName($report);
$s3Path = $automatedReportsService->getMediaPath($report);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$fileName ' . PHP_EOL . print_r($fileName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$typeName ' . PHP_EOL . print_r($typeName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$teamsName ' . PHP_EOL . print_r($teamsName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$periodName ' . PHP_EOL . print_r($periodName, true));
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$s3Path ' . PHP_EOL . print_r($s3Path, true));
$jobDispatcher->dispatch(
new SendReportMailJob(
reportUuid: $report->getUuid(),
s3Path: $s3Path,
recipientEmail: $recipient['email'],
recipientName: $recipient['name'] ?? null,
fileName: $fileName,
typeName: $typeName,
teamsName: $teamsName,
periodName: $periodName,
isAskJiminny: true,
)
);
exit(1);
}
private function formatDate(JobDispatcherInterface $jobDispatcher): void
{
$customName = 'Custom report name';
// $frequency = self::FREQUENCY_DAILY;
// $frequency = self::FREQUENCY_WEEKLY;
$frequency = self::FREQUENCY_MONTHLY;
// $frequency = self::FREQUENCY_QUARTERLY;
// $frequency = self::FREQUENCY_ONE_OFF;
$period = $this->calculateFromAndToDatePeriod($frequency);
$from = $period['fromDate'];
$to = $period['toDate'];
$periodName = $this->formatReportPeriodName($frequency, $from, $to);
$filenameSuffix = null;
if ($customName) {
if ($filenameSuffix) {
$customName .= " {$filenameSuffix}";
}
$result = $this->sanitizeFileName("{$customName} - {$periodName}");
}
$this->info($result);
}
public function calculateFromAndToDatePeriod(
string $frequency,
?Carbon $fromDate = null,
?Carbon $toDate = null
): array {
if ($frequency === self::FREQUENCY_ONE_OFF) {
return [
'fromDate' => $fromDate,
'toDate' => $toDate,
];
}
$now = Carbon::now();
return match ($frequency) {
self::FREQUENCY_DAILY => [
'fromDate' => $now->copy()->subDay()->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_WEEKLY => [
'fromDate' => $now->copy()->subWeeks(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_MONTHLY => [
'fromDate' => $now->copy()->subMonths(1)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
self::FREQUENCY_QUARTERLY => [
'fromDate' => $now->copy()->subMonths(3)->startOfDay(),
'toDate' => $now->copy()->subDay()->endOfDay(),
],
default => throw new InvalidArgumentException("Unsupported frequency: {$frequency}"),
};
}
private function formatReportPeriodName(string $frequency, Carbon $from, Carbon $to): string
{
$fromYear = $from->format('Y');
$toYear = $to->format('Y');
$differentYears = $fromYear !== $toYear;
switch ($frequency) {
case self::FREQUENCY_DAILY:
return $from->format('j M Y');
case self::FREQUENCY_QUARTERLY:
// 'Jan-Mar 2025' or 'Nov 2024-Jan 2025' if years differ
$startMonth = $from->format('M');
$endMonth = $to->copy()->subMonth();
$endMonthName = $endMonth->format('M');
$endMonthYear = $endMonth->format('Y');
if ($differentYears) {
return "{$startMonth} {$fromYear} - {$endMonthName} {$endMonthYear}";
}
return "{$startMonth} - {$endMonthName} {$toYear}";
case self::FREQUENCY_MONTHLY:
// 'May 2025' - monthly reports are always within the same year
return $from->format('M Y');
case self::FREQUENCY_WEEKLY:
// '4 - 8 Aug 2025', '27 Oct - 3 Nov 2025', or '28 Dec 2024 - 3 Jan 2025' if years differ
$startDay = $from->format('j');
$endDay = $to->format('j');
$startMonth = $from->format('M');
$endMonth = $to->format('M');
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
if ($startMonth !== $endMonth) {
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
}
return "{$startDay} - {$endDay} {$endMonth} {$toYear}";
case self::FREQUENCY_ONE_OFF:
// '2 May-31 May 2025' or '15 Dec 2024-15 Jan 2025' if years differ
$startDay = $from->format('j');
$startMonth = $from->format('M');
$endDay = $to->format('j');
$endMonth = $to->format('M');
// If same month and year, use a format like '2-31 May 2025'
if ($startMonth === $endMonth && ! $differentYears) {
return "{$startDay} - {$endDay} {$startMonth} {$toYear}";
}
// If different years, include both years
if ($differentYears) {
return "{$startDay} {$startMonth} {$fromYear} - {$endDay} {$endMonth} {$toYear}";
}
// Same year but different months
return "{$startDay} {$startMonth} - {$endDay} {$endMonth} {$toYear}";
default:
// Default format for unknown frequencies
return $from->format('j M Y') . ' - ' . $to->format('j M Y');
}
}
public function sanitizeFileName(string $fileName): string
{
return str_replace(['/', '\\'], '-', $fileName);
}
private function getPayload(AutomatedReportsService $automatedReportsService)
{
$reportResult = AutomatedReportResult::find(269);
$automatedReport = $reportResult->getReport();
$activityIds = [1,2,3];
$payload = $automatedReportsService->getAskJiminnyGenerateReportPayload(
automatedReport: $automatedReport,
reportResult: $reportResult,
activityIds: $activityIds,
);
\Illuminate\Support\Facades\Log::channel('custom_channel')->info('$payload ' . PHP_EOL . print_r($payload, true));
}
}
Sync Changes
Hide This Notification
Code changed:
Hide
36
Previous Highlighted Error
Next Highlighted Error
[2026-04-24 09:01:25] local.INFO: B {"correlation_id":"f5851610-eb8f-43c9-b2fa-d3acfb0608a6","trace_id":"e8f54fb0-0b73-4607-9247-494e6af5abc4"}
[2026-04-24 09:01:26] local.INFO: $result
Illuminate\Http\Client\Response Object
(
[response:protected] => GuzzleHttp\Psr7\Response Object
(
[reasonPhrase:GuzzleHttp\Psr7\Response:private] => Unauthorized
[statusCode:GuzzleHttp\Psr7\Response:private] => 401
[headers:GuzzleHttp\Psr7\Response:private] => Array
(
[Date] => Array
(
[0] => Fri, 24 Apr 2026 09:01:26 GMT
)
[Content-Type] => Array
(
[0] => application/json; charset=utf-8
)
[Content-Length] => Array
(
[0] => 186
)
[Connection] => Array
(
[0] => keep-alive
)
[cache-control] => Array
(
[0] => max-age=0, private, must-revalidate
)
[server] => Array
(
[0] => Cowboy
)
[x-ratelimit-limit] => Array
(
[0] => 600
)
[x-ratelimit-remaining] => Array
(
[0] => 599
)
[x-ratelimit-reset] => Array
(
[0] => 1777081286
)
[x-request-id] => Array
(
[0] => GKk_cdj69Q_svKyLwxwN
)
)
[headerNames:GuzzleHttp\Psr7\Response:private] => Array
(
[date] => Date
[content-type] => Content-Type
[content-length] => Content-Length
[connection] => Connection
[cache-control] => cache-control
[server] => server
[x-ratelimit-limit] => x-ratelimit-limit
[x-ratelimit-remaining] => x-ratelimit-remaining
[x-ratelimit-reset] => x-ratelimit-reset
[x-request-id] => x-request-id
)
[protocol:GuzzleHttp\Psr7\Response:private] => 1.1
[stream:GuzzleHttp\Psr7\Response:private] => GuzzleHttp\Psr7\Stream Object
(
[stream:GuzzleHttp\Psr7\Stream:private] => Resource id #2511
[size:GuzzleHttp\Psr7\Stream:private] =>
[seekable:GuzzleHttp\Psr7\Stream:private] => 1
[readable:GuzzleHttp\Psr7\Stream:private] => 1
[writable:GuzzleHttp\Psr7\Stream:private] => 1
[uri:GuzzleHttp\Psr7\Stream:private] => php://temp
[customMetadata:GuzzleHttp\Psr7\Stream:private] => Array
(
)
)
)
[decoded:protected] =>
[cookies] => GuzzleHttp\Cookie\CookieJar Object
(
[cookies:GuzzleHttp\Cookie\CookieJar:private] => Array
(
)
[strictMode:GuzzleHttp\Cookie\CookieJar:private] =>
)
[transferStats] => GuzzleHttp\TransferStats Object
(
[request:GuzzleHttp\TransferStats:private] => GuzzleHttp\Psr7\Request Object
(
[method:GuzzleHttp\Psr7\Request:private] => POST
[requestTarget:GuzzleHttp\Psr7\Request:private] =>
[uri:GuzzleHttp\Psr7\Request:private] => GuzzleHttp\Psr7\Uri Object
(
[scheme:GuzzleHttp\Psr7\Uri:private] => https
[userInfo:GuzzleHttp\Psr7\Uri:private] =>
[host:GuzzleHttp\Psr7\Uri:private] => analytex.userpilot.io
[port:GuzzleHttp\Psr7\Uri:private] =>
[path:GuzzleHttp\Psr7\Uri:private] => /v1/track
[query:GuzzleHttp\Psr7\Uri:private] =>
[fragment:GuzzleHttp\Psr7\Uri:private] =>
[composedComponents:GuzzleHttp\Psr7\Uri:private] => https://analytex.userpilot.io/v1/track
)
[headers:GuzzleHttp\Psr7\Request:private] => Array
(
[Content-Length] => Array
(
[0] => 156
)
[User-Agent] => Array
(
[0] => GuzzleHttp/7
)
[Content-Type] => Array
(
[0] => application/json
)
[Host] => Array
(
[0] => analytex.userpilot.io
)
[X-API-Version] => Array
(
[0] => 2020-09-22
)
[Authorization] => Array
(
[0] => Token
)
)
[headerNames:GuzzleHttp\Psr7\Request:private] => Array
(
[content-length] => Content-Length
[user-agent] => User-Agent
[content-type] => Content-Type
[host] => Host
[x-api-version] => X-API-Version
[authorization] => Authorization
)
[protocol:GuzzleHttp\Psr7\Request:private] => 1.1
[stream:GuzzleHttp\Psr7\Request:private] => GuzzleHttp\Psr7\Stream Object
(
[stream:GuzzleHttp\Psr7\Stream:private] => Resource id #2502
[size:GuzzleHttp\Psr7\Stream:private] => 156
[seekable:GuzzleHttp\Psr7\Stream:private] => 1
[readable:GuzzleHttp\Psr7\Stream:private] => 1
[writable:GuzzleHttp\Psr7\Stream:private] => 1
[uri:GuzzleHttp\Psr7\Stream:private] => php://temp
[customMetadata:GuzzleHttp\Psr7\Stream:private] => Array
(
)
)
)
[response:GuzzleHttp\TransferStats:private] => GuzzleHttp\Psr7\Response Object
(
[reasonPhrase:GuzzleHttp\Psr7\Response:private] => Unauthorized
[statusCode:GuzzleHttp\Psr7\Response:private] => 401
[headers:GuzzleHttp\Psr7\Response:private] => Array
(
[Date] => Array
(
[0] => Fri, 24 Apr 2026 09:01:26 GMT
)
[Content-Type] => Array
(
[0] => application/json; charset=utf-8
)
[Content-Length] => Array
(
[0] => 186
)
[Connection] => Array
(
[0] => keep-alive
)
[cache-control] => Array
(
[0] => max-age=0, private, must-revalidate
)
[server] => Array
(
[0] => Cowboy
)
[x-ratelimit-limit] => Array
(
[0] => 600
)
[x-ratelimit-remaining] => Array
(
[0] => 599
)
[x-ratelimit-reset] => Array
(
[0] => 1777081286
)
[x-request-id] => Array
(
[0] => GKk_cdj69Q_svKyLwxwN
)
)
[headerNames:GuzzleHttp\Psr7\Response:private] => Array
(
[date] => Date
[content-type] => Content-Type
[content-length] => Content-Length
[connection] => Connection
[cache-control] => cache-control
[server] => server
[x-ratelimit-limit] => x-ratelimit-limit
[x-ratelimit-remaining] => x-ratelimit-remaining
[x-ratelimit-reset] => x-ratelimit-reset
[x-request-id] => x-request-id
)
[protocol:GuzzleHttp\Psr7\Response:private] => 1.1
[stream:GuzzleHttp\Psr7\Response:private] => GuzzleHttp\Psr7\Stream Object
(
[stream:GuzzleHttp\Psr7\Stream:private] => Resource id #2511
[size:GuzzleHttp\Psr7\Stream:private] =>
[seekable:GuzzleHttp\Psr7\Stream:private] => 1
[readable:GuzzleHttp\Psr7\Stream:private] => 1
[writable:GuzzleHttp\Psr7\Stream:private] => 1
[uri:GuzzleHttp\Psr7\Stream:private] => php://temp
[customMetadata:GuzzleHttp\Psr7\Stream:private] => Array
(
)
)
)
[transferTime:GuzzleHttp\TransferStats:private] => 0.590833
[handlerStats:GuzzleHttp\TransferStats:private] => Array
(
[url] => https://analytex.userpilot.io/v1/track
[content_type] => application/json; charset=utf-8
[http_code] => 401
[header_size] => 345
[request_size] => 340
[filetime] => -1
[ssl_verify_result] => 0
[redirect_count] => 0
[total_time] => 0.590833
[namelookup_time] => 0.075913
[connect_time] => 0.243629
[pretransfer_time] => 0.418428
[size_upload] => 156
[size_download] => 186
[speed_download] => 314
[speed_upload] => 264
[download_content_length] => 186
[upload_content_length] => 156
[starttransfer_time] => 0.587514
[redirect_time] => 0
[redirect_url] =>
[primary_ip] => [IP_ADDRESS]
[certinfo] => Array
(
)
[primary_port] => 443
[local_ip] => [IP_ADDRESS]
[local_port] => 60852
[http_version] => 2
[protocol] => 2
[ssl_verifyresult] => 0
[scheme] => HTTPS
[appconnect_time_us] => 418310
[connect_time_us] => 243629
[namelookup_time_us] => 75913
[pretransfer_time_us] => 418428
[redirect_time_us] => 0
[starttransfer_time_us] => 587514
[total_time_us] => 590833
[effective_method] => POST
[capath] => /etc/ssl/certs
[cainfo] => /etc/ssl/certs/ca-certificates.crt
[appconnect_time] => 0.41831
)
[handlerErrorData:GuzzleHttp\TransferStats:private] => 0
)
[truncateExceptionsAt:protected] =>
)
{"correlation_id":"f5851610-eb8f-43c9-b2fa-d3acfb0608a6","trace_id":"e8f54fb0-0b73-4607-9247-494e6af5abc4"}
Project
Project
New File or Directory…
Expand Selected
Collapse All
Options
Hide...
|
PhpStorm
|
faVsco.js – custom.log
|
NULL
|
77263
|
|
48261
|
Project: faVsco.js, menu
JY-20692-fix-integration- Project: faVsco.js, menu
JY-20692-fix-integration-app-[API_KEY], menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
cachedStages
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
2/4
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
33
2
19
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot\ServiceTraits;
use Carbon\Carbon;
use HubSpot\Client\Crm\Deals\Model\CollectionResponseAssociatedId;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Models\Account;
use Exception;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Jobs\Crm\MatchActivitiesToNewOpportunity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\BusinessProcess;
use Jiminny\Exceptions\CrmException;
use Jiminny\Models\Opportunity;
use Illuminate\Support\Collection;
use Jiminny\Models\Stage;
use Jiminny\Repositories\Crm\CrmEntityRepository;
use Jiminny\Services\Crm\Hubspot\DealFieldsService;
use Jiminny\Services\Crm\Hubspot\OpportunitySyncStrategy\HubspotSingleSyncStrategy;
use Jiminny\Services\Crm\Hubspot\WebhookSyncBatchProcessor;
use Jiminny\Services\Crm\OpportunitySyncStrategyResolver;
use Jiminny\Utils\CurrencyFormatter;
/**
* Optimized sync methods for better performance
* These methods can be integrated into SyncCrmEntitiesTrait for significant performance gains
*/
trait OpportunitySyncTrait
{
private const int BATCH_SIZE = 100;
private const int BATCH_PROCESS_SIZE = 800;
protected OpportunitySyncStrategyResolver $opportunitySyncStrategyResolver;
protected CrmEntityRepository $crmEntityRepository;
protected DealFieldsService $dealFieldsService;
private ?array $cachedClosedDealStages = null;
private array $cachedBusinessProcesses = [];
private array $cachedStages = [];
public function syncOpportunities(array $parameters, ?string $strategy = null): int
{
$strategies = $this->opportunitySyncStrategyResolver->getStrategies($this->config, $strategy);
$parameters['config'] = $this->config;
$syncCount = 0;
$reportedTotal = 0;
$lastSyncedId = [];
try {
foreach ($strategies as $strategyName => $syncStrategy) {
$this->logger->info(
'[' . $this->getDisplayName() . '] Syncing opportunities using strategy: ' .
$strategyName
);
$total = 0;
$lastId = null;
$buffer = [];
// HubspotWebhookBatchSyncStrategy returns empty generator, this is for other strategies
foreach ($syncStrategy->fetchOpportunities($parameters, $total, $lastId) as $hsOpportunity) {
$buffer[] = $hsOpportunity;
// process every 800 rows (fits < 1 000 association limit)
if (\count($buffer) >= self::BATCH_PROCESS_SIZE) {
$syncCount += $this->processOpportunityBatch($buffer);
$buffer = [];
}
}
// leftovers
if ($buffer) {
$syncCount += $this->processOpportunityBatch($buffer);
}
$reportedTotal += $total;
$lastSyncedId = $lastId;
}
} catch (\HubSpot\Client\Crm\Deals\ApiException | CrmException $e) {
$this->handleSyncException($e, $parameters);
}
$this->logger->info(
'[HubSpot] Synced opportunities',
[
'team' => $this->team->getId(),
'sync_count' => $syncCount,
'total' => $reportedTotal,
'last_synced_id' => $lastSyncedId,
]
);
return $reportedTotal;
}
private function handleSyncException(\Throwable $e, array $parameters): void
{
if (($parameters['since'] ?? null) instanceof Carbon) {
$parameters['since'] = $parameters['since']->toDateTimeString();
}
$parameters['config'] = $this->config->getId();
$this->logger->warning('[' . $this->getDisplayName() . '] Sync opportunities failed', [
'teamId' => $this->team->getUuid(),
'parameters' => $parameters,
'reason' => $e->getMessage(),
]);
}
/**
* @inheritdoc
*/
public function syncOpportunity(string $crmId): ?Opportunity
{
$strategy = $this->opportunitySyncStrategyResolver->resolve(
$this->config,
OpportunitySyncStrategyResolver::SINGLE_SYNC_OPPORTUNITY_STRATEGY,
);
$parameters = [
'config' => $this->config,
'crm_id' => $crmId,
];
try {
if (! $strategy instanceof HubspotSingleSyncStrategy) {
throw new InvalidArgumentException('Strategy must by HubspotSingleSyncStrategy');
}
$hsOpportunity = $strategy->fetchOpportunity($parameters);
} catch (\HubSpot\Client\Crm\Deals\ApiException $e) {
$this->logger->info('[' . $this->getDisplayName() . '] Opportunity not found', [
'teamId' => $this->team->getUuid(),
'crmId' => $crmId,
'reason' => $e->getMessage(),
]);
return null;
}
$hsOpportunity['associations'] = $this->convertDealAssociations($hsOpportunity['associations'] ?? []);
return $this->importOrUpdateOpportunity($hsOpportunity);
}
/**
* Process webhook-collected opportunity batches.
*
* Drains Redis sets containing company CRM IDs collected from webhook events
* and dispatches ImportOpportunityBatch jobs for batch processing.
*
* @return int Number of opportunity IDs dispatched to jobs
*/
public function batchSyncOpportunities(): int
{
$configId = $this->team->getCrmConfiguration()->getId();
return $this->batchProcessor->processBatchesForObjectType(
WebhookSyncBatchProcessor::OBJECT_TYPE_DEAL,
$configId
);
}
/**
* Import a batch of opportunities by their CRM IDs.
* Fetches opportunity data from HubSpot API and delegates to importOpportunityBatch().
*
* @param array<string> $crmIds HubSpot deal CRM IDs
*
* @return array{success: array, failed_ids: array, errors?: array<string, string>}
*/
public function importOpportunityBatchByIds(array $crmIds): array
{
$fields = $this->dealFieldsService->getFieldsForConfiguration($this->config);
$allDeals = [];
foreach (array_chunk($crmIds, self::BATCH_SIZE) as $chunk) {
$deals = $this->client->getOpportunitiesByIds($chunk, $fields);
foreach ($deals as $deal) {
$allDeals[] = $deal;
}
}
// IDs not returned by HubSpot are likely deleted or inaccessible deals.
// These are not failures — retrying won't bring them back.
$fetchedIds = array_map('strval', array_column($allDeals, 'id'));
$notFoundIds = array_values(array_diff(array_map('strval', $crmIds), $fetchedIds));
if (! empty($notFoundIds)) {
$this->logger->info('[' . $this->getDisplayName() . '] CRM IDs not found in HubSpot (likely deleted)', [
'teamId' => $this->team->getId(),
'notFoundCount' => \count($notFoundIds),
'notFoundIds' => $notFoundIds,
'requestedCount' => \count($crmIds),
'fetchedCount' => \count($allDeals),
]);
}
if (empty($allDeals)) {
return ['success' => [], 'failed_ids' => []];
}
return $this->importOpportunityBatch($allDeals);
}
private function getClosedDealStages(): array
{
if ($this->cachedClosedDealStages !== null) {
return $this->cachedClosedDealStages;
}
$stages = $this->crmEntityRepository->getOpportunityClosedStages($this->config);
$data = [
'lost' => [],
'won' => [],
];
foreach ($stages as $stage) {
if ($stage->probability == 0.00) {
$data['lost'][] = $stage->crm_provider_id;
}
if ($stage->probability == 100.00) {
$data['won'][] = $stage->crm_provider_id;
}
}
$this->cachedClosedDealStages = $data;
return $data;
}
/**
* Import deals into the database with pre-fetched associations.
*
* API calls here (getAssociationsData, getExistingOpportunityCrmIds) are NOT
* caught — if they throw, the exception propagates to ImportOpportunityBatch::handle()
* where Laravel retries the whole job with backoff. After all retries exhausted,
* failed() requeues all IDs to Redis.
*
* The per-deal loop catches exceptions individually. A deal can end up in three states:
* - success: imported/updated successfully
* - failed_ids: exception thrown (DB constraint violation, corrupt data, etc.)
* These are permanent issues — retrying won't fix them.
* - skipped (null): missing dependencies (no account, unknown pipeline/stage).
* This is acceptable — the deal cannot be imported until those exist.
*/
private function importOpportunityBatch(array $deals): array
{
$syncedOpportunities = [
'success' => [],
'failed_ids' => [],
];
$dealIds = array_column($deals, 'id');
// Shared association/existing-ID preparation is batch-level state. If it fails, rethrow so the
// queue job retries the whole batch and eventually requeues all deal IDs back to Redis.
try {
$companyAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'companies');
$contactAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'contacts');
$associationsData = $this->prepareAssociatedEntities($companyAssociations, $contactAssociations);
$existingCrmIds = $this->crmEntityRepository->getExistingOpportunityCrmIds(
$this->config,
array_map('strval', $dealIds)
);
$existingCrmIdSet = array_flip($existingCrmIds);
} catch (\Throwable $e) {
$this->logger->error('[' . $this->getDisplayName() . '] Failed to fetch associations or existing IDs', [
'teamId' => $this->team->getId(),
'dealCount' => count($dealIds),
'error' => $e->getMessage(),
]);
throw $e;
}
foreach ($deals as $deal) {
try {
$deal['associations'] = $this->prepareAssociationsForOpportunity(
$deal['id'],
$companyAssociations,
$contactAssociations,
$associationsData
);
$syncedOpportunity = $this->importOrUpdateOpportunity(
$deal,
isset($existingCrmIdSet[(string) $deal['id']])
);
if ($syncedOpportunity) {
$syncedOpportunities['success'][] = $syncedOpportunity;
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import opportunity', [
'teamId' => $this->team->getId(),
'crmId' => $deal['id'],
'error' => $e->getMessage(),
]);
$syncedOpportunities['failed_ids'][] = $deal['id'];
$syncedOpportunities['errors'][$deal['id']] = $e->getMessage();
}
}
return $syncedOpportunities;
}
/**
* Prepare associated entities for opportunities with optimized batch processing
* Returns structured data with CRM ID to DB ID mappings for each opportunity
*/
private function prepareAssociatedEntities(array $companyAssociations, array $contactAssociations): array
{
// Step 1: Collect all unique company and contact IDs from associations
$allCompanyIds = $this->flattenAssociationIds($companyAssociations);
$allContactIds = $this->flattenAssociationIds($contactAssociations);
// Step 2: Batch sync missing entities and get CRM ID to DB ID mappings
$companyIdMappings = [];
$contactIdMappings = [];
if (! empty($allCompanyIds)) {
$companyIdMappings = $this->prepareAssociatedAccounts($allCompanyIds);
}
if (! empty($allContactIds)) {
$contactIdMappings = $this->prepareAssociatedContacts($allContactIds);
}
return [
'company_id_mappings' => $companyIdMappings,
'contact_id_mappings' => $contactIdMappings,
];
}
/**
* Flatten association data to get unique IDs
*/
private function flattenAssociationIds(array $associations): array
{
$ids = [];
foreach ($associations as $dealAssociations) {
if (is_array($dealAssociations)) {
foreach ($dealAssociations as $id) {
$ids[$id] = true;
}
}
}
return array_keys($ids);
}
/**
* Batch sync missing accounts
*/
private function prepareAssociatedAccounts(array $companyIds): array
{
// Find which accounts already exist
$existingAccounts = $this->crmEntityRepository
->findAccountsByExternalIds($this->config, $companyIds);
$existingCompanyIds = $existingAccounts->pluck('crm_provider_id')->toArray();
$existingAccountsData = $existingAccounts->mapWithKeys(function ($account) {
return [$account->getCrmProviderId() => $account->getId()];
})->toArray();
$missingCompanyIds = array_diff($companyIds, $existingCompanyIds);
if (empty($missingCompanyIds)) {
return $existingAccountsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing accounts', [
'teamId' => $this->team->getUuid(),
'total_companies' => count($companyIds),
'existing_companies' => count($existingCompanyIds),
'missing_companies' => count($missingCompanyIds),
]);
// we already have limit on opportunity ids count
// Initialize variable before try block
$syncedAccountsData = [];
try {
$syncedAccountsData = $this->batchSyncCrmObjects('companies', $missingCompanyIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing accounts', [
'size' => count($missingCompanyIds),
'error' => $e->getMessage(),
]);
$syncedAccountsData = [];
}
return $existingAccountsData + $syncedAccountsData;
}
/**
* Prepare associated contacts - find existing and sync missing ones
* Returns mapping of CRM ID to DB ID
*/
private function prepareAssociatedContacts(array $contactIds): array
{
// Find which contacts already exist
$existingContacts = $this->crmEntityRepository
->findContactsByExternalIds($this->config, $contactIds);
$existingContactIds = $existingContacts->pluck('crm_provider_id')->toArray();
// Create mapping for existing contacts
$existingContactsData = $existingContacts->mapWithKeys(function ($contact) {
return [$contact->getCrmProviderId() => $contact->getId()];
})->toArray();
$missingContactIds = array_diff($contactIds, $existingContactIds);
if (empty($missingContactIds)) {
return $existingContactsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing contacts', [
'teamId' => $this->team->getUuid(),
'total_contacts' => count($contactIds),
'existing_contacts' => count($existingContactIds),
'missing_contacts' => count($missingContactIds),
]);
// Sync missing contacts using batch API
try {
$syncedContactsData = $this->batchSyncCrmObjects('contacts', $missingContactIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing contacts', [
'size' => count($missingContactIds),
'error' => $e->getMessage(),
]);
$syncedContactsData = [];
}
return $existingContactsData + $syncedContactsData;
}
private function batchSyncCrmObjects(string $objectType, array $crmIds): array
{
$syncObjects = [];
$crmObjectIds = array_values($crmIds);
foreach (array_chunk($crmObjectIds, self::BATCH_SIZE) as $chunk) {
try {
$objects = $objectType === 'companies' ?
$this->client->getCompaniesByIds($chunk, $this->getCompanyFields()) :
$this->client->getContactsByIds($chunk, $this->getContactFields());
foreach ($objects as $objectId => $objectData) {
$this->importCrmObject($objectType, (string) $objectId, $objectData, $syncObjects);
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch synced ' . $objectType, [
'requested_count' => count($chunk),
'synced_count' => count($objects),
]);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Batch ' . $objectType . ' sync failed', [
'ids' => $chunk,
'error' => $e->getMessage(),
]);
}
}
return $syncObjects;
}
private function importCrmObject(string $objectType, string $objectId, mixed $objectData, array &$syncObjects): void
{
try {
$object = $objectType === 'companies' ?
$this->importAccount($objectData) :
$this->importContact($objectData);
if ($object) {
$syncObjects[$object->getCrmProviderId()] = $object->getId();
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import batch ' . $objectType, [
'id' => $objectId,
'error' => $e->getMessage(),
]);
}
}
/**
* Prepare associations for a single opportunity
*
* The return value is an array with the following structure:
* [
* 'companies' => [
* $companyCrmId => $companyId,
* ...
* ],
* 'contacts' => [
* $contactCrmId => $contactId,
* ...
* ],
* 'account_id' => $accountId,
* ]
*/
private function prepareAssociationsForOpportunity(
string $oppCrmId,
array $companyAssociations,
array $contactAssociations,
array $associationsData
): array {
$associations = [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
$oppCompanyIds = $companyAssociations[$oppCrmId] ?? [];
foreach ($oppCompanyIds as $companyCrmId) {
if (isset($associationsData['company_id_mappings'][$companyCrmId])) {
$associations['companies'][$companyCrmId] = $associationsData['company_id_mappings'][$companyCrmId];
// Set primary account (first company becomes primary account)
if ($associations['account_id'] === null) {
$associations['account_id'] = $associationsData['company_id_mappings'][$companyCrmId];
}
}
}
$oppContactIds = $contactAssociations[$oppCrmId] ?? [];
foreach ($oppContactIds as $contactCrmId) {
if (isset($associationsData['contact_id_mappings'][$contactCrmId])) {
$associations['contacts'][$contactCrmId] = $associationsData['contact_id_mappings'][$contactCrmId];
}
}
return $associations;
}
/**
* Update only associations for an opportunity
*/
private function updateOpportunityAssociations(Opportunity $opportunity, array $associations): void
{
// Update contact associations
$this->importOpportunityContacts($opportunity, $associations['contacts']);
// Update company (account) associations
$this->updateOpportunityAccount($opportunity, $associations['account_id']);
}
/**
* Remove all contact associations from an opportunity
*/
private function removeAllOpportunityContacts(Opportunity $opportunity): void
{
$currentCount = (int) $opportunity->contacts()->count();
if ($currentCount > 0) {
$opportunity->contacts()->detach();
$this->logger->info('[' . $this->getDisplayName() . '] Removed all contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_count' => $currentCount,
]);
}
}
private function updateOpportunityAccount(Opportunity $opportunity, ?int $accountId): void
{
if ($accountId === null) {
// No account ID provided - keep current account
return;
}
$currentAccountId = $opportunity->getAccountId();
// Only update if account has changed
if ($currentAccountId !== $accountId) {
$opportunity->account_id = $accountId;
$opportunity->save();
$this->logger->info('[' . $this->getDisplayName() . '] Updated opportunity account association', [
'opportunity_id' => $opportunity->getId(),
'old_account_id' => $currentAccountId,
'new_account_id' => $accountId,
]);
}
}
/**
* Find existing opportunities by external IDs (OPTIMIZED VERSION)
* Uses batch query for better performance
*/
private function findExistingOpportunities(array $crmIds): Collection
{
return $this->crmEntityRepository
->findOpportunitiesByExternalIds($this->config, $crmIds);
}
private function processOpportunityBatch(array $opportunities): int
{
$syncedOpportunities = $this->importOpportunityBatch($opportunities);
return count($syncedOpportunities['success'] ?? []);
}
/**
* Convert single deal associations from HubSpot format to internal format
* Handles both HubSpot SDK objects and array formats
*
* @param array $opportunityAssociations Raw associations from HubSpot API or pre-processed
*
* @return array Processed associations with DB IDs
*/
private function convertDealAssociations(array $opportunityAssociations): array
{
$associations = $this->initializeAssociationsStructure();
if (empty($opportunityAssociations)) {
return $associations;
}
$associationIds = $this->extractAssociationIds($opportunityAssociations);
$this->processCompanyAssociations($associationIds, $associations);
$this->processContactAssociations($associationIds, $associations);
return $associations;
}
private function initializeAssociationsStructure(): array
{
return [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
}
private function extractAssociationIds(array $opportunityAssociations): array
{
$associationIds = [];
foreach ($opportunityAssociations as $type => $associationData) {
if (! empty($associationData)) {
$associationIds[$type] = $this->convertSingleDealAssociations($associationData);
}
}
return $associationIds;
}
private function processCompanyAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['companies'])) {
return;
}
$companyId = $associationIds['companies'][0];
$account = $this->findOrSyncAccount($companyId);
if ($account instanceof Account) {
$associations['companies'][$companyId] = $account->getId();
$associations['account_id'] = $account->getId();
}
}
private function processContactAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['contacts'])) {
return;
}
foreach ($associationIds['contacts'] as $contactId) {
$contact = $this->findOrSyncContact($contactId);
if ($contact instanceof Contact) {
$associations['contacts'][$contactId] = $contact->getId();
}
}
}
private function findOrSyncAccount(string $companyId): ?Account
{
$account = $this->crmEntityRepository->findAccountByExternalId($this->config, $companyId);
if (! $account instanceof Account) {
$account = $this->syncAccount($companyId);
}
return $account;
}
private function findOrSyncContact(string $contactId): ?Contact
{
$contact = $this->crmEntityRepository->findContactByExternalId($this->config, $contactId);
if (! $contact instanceof Contact) {
$contact = $this->syncContact($contactId);
}
return $contact;
}
private function convertSingleDealAssociations($opportunityAssociations = null): array
{
$associationData = [];
if ($opportunityAssociations === null) {
return $associationData;
}
// Handle array input (from extractAssociationIds)
if (is_array($opportunityAssociations)) {
return $opportunityAssociations;
}
// Handle CollectionResponseAssociatedId object
if ($opportunityAssociations instanceof CollectionResponseAssociatedId) {
foreach ($opportunityAssociations->getResults() as $association) {
$associationData[] = $association->getId();
}
}
return $associationData;
}
private function importOrUpdateOpportunity($crmData, ?bool $exists = null): ?Opportunity
{
if (empty($crmData['properties'])) {
return null;
}
$crmId = (string) $crmData['id'];
$properties = $crmData['properties'];
$associations = $crmData['associations'] ?? [];
$opportunityExists = $exists ?? (bool) $this->crmEntityRepository->findOpportunityByExternalId(
$this->config,
$crmId
);
if ($opportunityExists) {
return $this->updateOpportunity($crmId, $properties, $associations);
} else {
return $this->createOpportunity($crmId, $properties, $associations);
}
}
/**
* Create new opportunity
*/
private function createOpportunity(string $crmId, array $properties, array $associations): ?Opportunity
{
$accountId = $this->resolveAccountId($associations);
if (! $accountId) {
return null;
}
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
if (! $businessProcess) {
return null;
}
$stage = $this->resolveStage($businessProcess, $properties['dealstage'] ?? null);
if (! $stage) {
return null;
}
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->importOpportunityContacts($opportunity, $associations['contacts']);
if ($opportunity->wasRecentlyCreated) {
MatchActivitiesToNewOpportunity::dispatch($opportunity->getId());
}
return $opportunity;
}
/**
* Update existing opportunity
*/
private function updateOpportunity(string $crmId, array $properties, array $associations): Opportunity
{
$accountId = $this->resolveAccountId($associations);
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
$stage = $businessProcess ? $this->resolveStage($businessProcess, $properties['dealstage'] ?? null) : null;
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->updateOpportunityAssociations($opportunity, $associations);
return $opportunity;
}
private function resolveAccountId(array $associations): ?int
{
if (! empty($associations['accountId'])) {
return $associations['accountId'];
}
if (empty($associations)) {
return null;
}
// we can't resolve multiple account ids (currently SDK returns one company)
foreach ($associations['companies'] as $accountId) {
return $accountId;
}
return null;
}
private function buildOpportunityData(
array $properties,
?int $accountId,
?BusinessProcess $businessProcess,
?Stage $stage
): array {
$ownerId = null;
$profile = null;
if (! empty($properties['hubspot_owner_id'])) {
$ownerId = $properties['hubspot_owner_id'];
$profile = $this->crmEntityRepository->findProfileByExternalId($this->config, (string) $ownerId);
}
$name = 'Unknown';
if (isset($properties['dealname'])) {
$name = mb_strimwidth($properties['dealname'], 0, 128);
}
$amount = $this->resolveAmount($properties);
$currency = $properties['deal_currency_code'] ?? null;
$closeDate = null;
if (! empty($properties['closedate'])) {
$closeDate = Carbon::parse($properties['closedate'])->format('Y-m-d');
}
$remotelyCreatedAt = null;
if (! empty($properties['createdate']) && strtotime($properties['createdate'])) {
$date = $this->parseCleanDatetime($properties['createdate']);
$remotelyCreatedAt = $date?->format('Y-m-d H:i:s');
}
$closedStages = $this->getClosedDealStages();
$isWon = in_array($properties['dealstage'], $closedStages['won']);
$isLost = in_array($properties['dealstage'], $closedStages['lost']);
$data = [
'team_id' => $this->team->getId(),
'user_id' => $profile ? $profile->user_id : null,
'owner_id' => $ownerId,
'name' => $name,
'value' => ! empty($amount) ? $amount : null,
'currency_code' => CurrencyFormatter::formatCode($currency),
'close_date' => $closeDate,
'is_closed' => $isWon || $isLost,
'is_won' => $isWon,
'remotely_created_at' => $remotelyCreatedAt,
'probability' => $this->resolveDealProbability($properties['hs_deal_stage_probability']),
'forecast_category' => $this->resolveForecastCategory($properties['hs_manual_forecast_category']),
];
if ($accountId) {
$data['account_id'] = $accountId;
}
if ($stage) {
$data['stage_id'] = $stage->id;
}
if ($businessProcess) {
$recordType = $this->crmEntityRepository->getBusinessProcessRecordType($businessProcess);
if ($recordType) {
$data['record_type_id'] = $recordType->id;
}
}
return $data;
}
private function resolveBusinessProcess(?string $pipelineId): ?BusinessProcess
{
if ($pipelineId === null) {
return null;
}
if (isset($this->cachedBusinessProcesses[$pipelineId])) {
return $this->cachedBusinessProcesses[$pipelineId];
}
$businessProcess = $this->getBusinessProcess($pipelineId);
if (! $businessProcess instanceof BusinessProcess) {
$this->importStages();
$businessProcess = $this->getBusinessProcess($pipelineId);
}
if (! $businessProcess instanceof BusinessProcess) {
$this->logger->info(
'[HubSpot] Deal is not attached to a pipeline',
[
'pipeline' => $pipelineId]
);
}
$this->cachedBusinessProcesses[$pipelineId] = $businessProcess;
return $businessProcess;
}
private function getBusinessProcess(string $pipelineId): ?BusinessProcess
{
return $this->crmEntityRepository->findBusinessProcessesByExternalId($this->config, $pipelineId);
}
private function resolveStage(BusinessProcess $businessProcess, ?string $stageId): ?Stage
{
if (empty($stageId)) {
return null;
}
$cacheKey = $businessProcess->getId() . ':' . $stageId;
if (isset($this->cachedStages[$cacheKey])) {
return $this->cachedStages[$cacheKey];
}
$stage = $this->crmEntityRepository->getPipelineStageByConditions(
$businessProcess,
[
'crm_provider_id' => $stageId,
'type' => Stage::TYPE_OPPORTUNITY,
]
);
if ($stage === null) {
$this->importStages(null, $stageId);
}
if ($stage === null) {
$this->logger->info('[HubSpot] Stage does not exist => ' . $stageId);
}
$this->cachedStages[$cacheKey] = $stage;
return $stage;
}
private function resolveAmount(array $properties): ?string
{
$amount = null;
if (! empty($properties['amount'])) {
$amount = str_replace(',', '', $properties['amount']);
}
if ($this->config->hasDefaultCurrencyFieldSet()) {
$valueFieldName = $this->config->getDefaultCurrencyField()->getCrmProviderId();
$amount = $properties[$valueFieldName] ?? $amount;
}
return $amount;
}
private function parseCleanDatetime(string $datetime): ?Carbon
{
// Treat pre-1980 values as invalid
$minValidDate = Carbon::parse('1980-01-01 00:00:00');
try {
$date = Carbon::parse($datetime);
if ($minValidDate->gt($date)) {
return null;
}
return $date;
} catch (Exception) {
return null; // On parse error, treat as null
}
}
private function resolveDealProbability(?string $stageProbability): int
{
if ($stageProbability === null) {
return 0;
}
$probability = (float) $stageProbability;
return $probability > 1 ? 0 : (int) ($probability * 100);
}
private function resolveForecastCategory(?string $forecastCategory): string
{
if (! $forecastCategory) {
return Forecast::FORECAST_CATEGORY_UNCATEGORIZED;
}
$forecastCategory = str_replace('_', ' ', $forecastCategory);
return ucwords(strtolower($forecastCategory));
}
private function importExternalFieldData(array $properties, int $opportunityId): void
{
$crmFields = $this->getOpportunitySyncableFields();
$this->importOpportunityCrmFieldData($properties, $crmFields, $opportunityId);
}
private function importOpportunityContacts(Opportunity $opportunity, array $associations): void
{
// Handle empty or missing contact associations
if (empty($associations)) {
// Remove all existing contact associations if none provided
$this->removeAllOpportunityContacts($opportunity);
return;
}
// Use differential sync approach for better performance and accuracy
$this->syncOpportunityContactsDifferential($opportunity, $associations);
}
/**
* Sync opportunity contacts using differential approach
* This compares current vs new associations and only makes necessary changes
*/
private function syncOpportunityContactsDifferential(Opportunity $opportunity, array $contactAssociations): void
{
$currentContactCrmIds = $this->getCurrentContactCrmIds($opportunity);
$contactAssociationIds = array_keys($contactAssociations);
$contactsToAdd = array_diff($contactAssociationIds, $currentContactCrmIds);
$contactsToRemove = array_diff($currentContactCrmIds, $contactAssociationIds);
if (empty($contactsToAdd) && empty($contactsToRemove)) {
return;
}
$this->logContactAssociationChanges($opportunity, $currentContactCrmIds, $contactAssociations, $contactsToAdd, $contactsToRemove);
$this->removeContactAssociations($opportunity, $contactsToRemove);
$this->addContactAssociations($opportunity, $contactsToAdd, $contactAssociations);
}
private function getCurrentContactCrmIds(Opportunity $opportunity): array
{
return $opportunity->contacts()
->pluck('contacts.crm_provider_id')
->toArray();
}
private function logContactAssociationChanges(
Opportunity $opportunity,
array $currentContactCrmIds,
array $contactAssociations,
array $contactsToAdd,
array $contactsToRemove
): void {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association changes', [
'opportunity_id' => $opportunity->getId(),
'current_contacts' => $currentContactCrmIds,
'new_contacts' => $contactAssociations,
'contacts_to_add' => $contactsToAdd,
'contacts_to_remove' => $contactsToRemove,
]);
}
private function removeContactAssociations(Opportunity $opportunity, array $contactsToRemove): void
{
if (empty($contactsToRemove)) {
return;
}
$contactsToDetach = $opportunity->contacts()
->whereIn('contacts.crm_provider_id', $contactsToRemove)
->pluck('contacts.id')
->toArray();
if (! empty($contactsToDetach)) {
$opportunity->contacts()->detach($contactsToDetach);
$this->logger->info('[' . $this->getDisplayName() . '] Removed contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_contact_crm_ids' => $contactsToRemove,
'removed_contact_count' => count($contactsToDetach),
]);
}
}
private function addContactAssociations(Opportunity $opportunity, array $contactsToAdd, array $contactAssociations): void
{
if (empty($contactsToAdd)) {
return;
}
$contactsAdded = [];
foreach ($contactsToAdd as $crmId) {
$id = $contactAssociations[$crmId];
if ($this->attachSingleContact($opportunity, (string) $crmId, $id)) {
$contactsAdded[] = $crmId;
}
}
$this->logAddedContacts($opportunity, $contactsAdded);
}
private function attachSingleContact(Opportunity $opportunity, string $crmId, int $id): bool
{
try {
$contact = $this->crmEntityRepository->findContactByConfigurationAndId($this->config, $id);
if (! $contact) {
return false;
}
return $this->performContactAttachment($opportunity, $contact, $crmId);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to add contact association', [
'opportunity_id' => $opportunity->getId(),
'contact_crm_id' => $crmId,
'error' => $e->getMessage(),
]);
return false;
}
}
private function performContactAttachment(Opportunity $opportunity, Contact $contact, string $crmId): bool
{
try {
$opportunity->contacts()->attach($contact->getId(), [
'crm_provider_id' => $crmId,
]);
return true;
} catch (\Illuminate\Database\QueryException $e) {
if (str_contains($e->getMessage(), 'Duplicate entry')) {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association already exists', [
'contact_id' => $contact->getId(),
'contact_crm_id' => $crmId,
'opportunity_id' => $opportunity->getId(),
]);
return false;
}
throw $e;
}
}
private function logAddedContacts(Opportunity $opportunity, array $contactsAdded): void
{
if (! empty($contactsAdded)) {
$this->logger->info('[' . $this->getDisplayName() . '] Added contact associations', [
'opportunity_id' => $opportunity->getId(),
'contacts_to_add_count' => count($contactsAdded),
'added_contact_crm_ids' => $contactsAdded,
'added_contacts_count' => count($contactsAdded),
]);
}
}
}...
|
PhpStorm
|
faVsco.js – crm_configurations [PROD]
|
NULL
|
48261
|
|
48262
|
Project: faVsco.js, menu
JY-20692-fix-integration- Project: faVsco.js, menu
JY-20692-fix-integration-app-[API_KEY], menu
Start Listening for PHP Debug Connections
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Show Replace Field
Search History
cachedStages
New Line
Match Case
Words
Regex
Replace History
Replace
New Line
Preserve case
2/4
Previous Occurrence
Next Occurrence
Filter Search Results
Open in Window, Multiple Cursors
Click to highlight
Close
Code changed:
Hide
Sync Changes
Hide This Notification
33
2
19
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Services\Crm\Hubspot\ServiceTraits;
use Carbon\Carbon;
use HubSpot\Client\Crm\Deals\Model\CollectionResponseAssociatedId;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Models\Account;
use Exception;
use Jiminny\Component\DealInsights\Forecast\Forecast;
use Jiminny\Jobs\Crm\MatchActivitiesToNewOpportunity;
use Jiminny\Models\Contact;
use Jiminny\Models\Crm\BusinessProcess;
use Jiminny\Exceptions\CrmException;
use Jiminny\Models\Opportunity;
use Illuminate\Support\Collection;
use Jiminny\Models\Stage;
use Jiminny\Repositories\Crm\CrmEntityRepository;
use Jiminny\Services\Crm\Hubspot\DealFieldsService;
use Jiminny\Services\Crm\Hubspot\OpportunitySyncStrategy\HubspotSingleSyncStrategy;
use Jiminny\Services\Crm\Hubspot\WebhookSyncBatchProcessor;
use Jiminny\Services\Crm\OpportunitySyncStrategyResolver;
use Jiminny\Utils\CurrencyFormatter;
/**
* Optimized sync methods for better performance
* These methods can be integrated into SyncCrmEntitiesTrait for significant performance gains
*/
trait OpportunitySyncTrait
{
private const int BATCH_SIZE = 100;
private const int BATCH_PROCESS_SIZE = 800;
protected OpportunitySyncStrategyResolver $opportunitySyncStrategyResolver;
protected CrmEntityRepository $crmEntityRepository;
protected DealFieldsService $dealFieldsService;
private ?array $cachedClosedDealStages = null;
private array $cachedBusinessProcesses = [];
private array $cachedStages = [];
public function syncOpportunities(array $parameters, ?string $strategy = null): int
{
$strategies = $this->opportunitySyncStrategyResolver->getStrategies($this->config, $strategy);
$parameters['config'] = $this->config;
$syncCount = 0;
$reportedTotal = 0;
$lastSyncedId = [];
try {
foreach ($strategies as $strategyName => $syncStrategy) {
$this->logger->info(
'[' . $this->getDisplayName() . '] Syncing opportunities using strategy: ' .
$strategyName
);
$total = 0;
$lastId = null;
$buffer = [];
// HubspotWebhookBatchSyncStrategy returns empty generator, this is for other strategies
foreach ($syncStrategy->fetchOpportunities($parameters, $total, $lastId) as $hsOpportunity) {
$buffer[] = $hsOpportunity;
// process every 800 rows (fits < 1 000 association limit)
if (\count($buffer) >= self::BATCH_PROCESS_SIZE) {
$syncCount += $this->processOpportunityBatch($buffer);
$buffer = [];
}
}
// leftovers
if ($buffer) {
$syncCount += $this->processOpportunityBatch($buffer);
}
$reportedTotal += $total;
$lastSyncedId = $lastId;
}
} catch (\HubSpot\Client\Crm\Deals\ApiException | CrmException $e) {
$this->handleSyncException($e, $parameters);
}
$this->logger->info(
'[HubSpot] Synced opportunities',
[
'team' => $this->team->getId(),
'sync_count' => $syncCount,
'total' => $reportedTotal,
'last_synced_id' => $lastSyncedId,
]
);
return $reportedTotal;
}
private function handleSyncException(\Throwable $e, array $parameters): void
{
if (($parameters['since'] ?? null) instanceof Carbon) {
$parameters['since'] = $parameters['since']->toDateTimeString();
}
$parameters['config'] = $this->config->getId();
$this->logger->warning('[' . $this->getDisplayName() . '] Sync opportunities failed', [
'teamId' => $this->team->getUuid(),
'parameters' => $parameters,
'reason' => $e->getMessage(),
]);
}
/**
* @inheritdoc
*/
public function syncOpportunity(string $crmId): ?Opportunity
{
$strategy = $this->opportunitySyncStrategyResolver->resolve(
$this->config,
OpportunitySyncStrategyResolver::SINGLE_SYNC_OPPORTUNITY_STRATEGY,
);
$parameters = [
'config' => $this->config,
'crm_id' => $crmId,
];
try {
if (! $strategy instanceof HubspotSingleSyncStrategy) {
throw new InvalidArgumentException('Strategy must by HubspotSingleSyncStrategy');
}
$hsOpportunity = $strategy->fetchOpportunity($parameters);
} catch (\HubSpot\Client\Crm\Deals\ApiException $e) {
$this->logger->info('[' . $this->getDisplayName() . '] Opportunity not found', [
'teamId' => $this->team->getUuid(),
'crmId' => $crmId,
'reason' => $e->getMessage(),
]);
return null;
}
$hsOpportunity['associations'] = $this->convertDealAssociations($hsOpportunity['associations'] ?? []);
return $this->importOrUpdateOpportunity($hsOpportunity);
}
/**
* Process webhook-collected opportunity batches.
*
* Drains Redis sets containing company CRM IDs collected from webhook events
* and dispatches ImportOpportunityBatch jobs for batch processing.
*
* @return int Number of opportunity IDs dispatched to jobs
*/
public function batchSyncOpportunities(): int
{
$configId = $this->team->getCrmConfiguration()->getId();
return $this->batchProcessor->processBatchesForObjectType(
WebhookSyncBatchProcessor::OBJECT_TYPE_DEAL,
$configId
);
}
/**
* Import a batch of opportunities by their CRM IDs.
* Fetches opportunity data from HubSpot API and delegates to importOpportunityBatch().
*
* @param array<string> $crmIds HubSpot deal CRM IDs
*
* @return array{success: array, failed_ids: array, errors?: array<string, string>}
*/
public function importOpportunityBatchByIds(array $crmIds): array
{
$fields = $this->dealFieldsService->getFieldsForConfiguration($this->config);
$allDeals = [];
foreach (array_chunk($crmIds, self::BATCH_SIZE) as $chunk) {
$deals = $this->client->getOpportunitiesByIds($chunk, $fields);
foreach ($deals as $deal) {
$allDeals[] = $deal;
}
}
// IDs not returned by HubSpot are likely deleted or inaccessible deals.
// These are not failures — retrying won't bring them back.
$fetchedIds = array_map('strval', array_column($allDeals, 'id'));
$notFoundIds = array_values(array_diff(array_map('strval', $crmIds), $fetchedIds));
if (! empty($notFoundIds)) {
$this->logger->info('[' . $this->getDisplayName() . '] CRM IDs not found in HubSpot (likely deleted)', [
'teamId' => $this->team->getId(),
'notFoundCount' => \count($notFoundIds),
'notFoundIds' => $notFoundIds,
'requestedCount' => \count($crmIds),
'fetchedCount' => \count($allDeals),
]);
}
if (empty($allDeals)) {
return ['success' => [], 'failed_ids' => []];
}
return $this->importOpportunityBatch($allDeals);
}
private function getClosedDealStages(): array
{
if ($this->cachedClosedDealStages !== null) {
return $this->cachedClosedDealStages;
}
$stages = $this->crmEntityRepository->getOpportunityClosedStages($this->config);
$data = [
'lost' => [],
'won' => [],
];
foreach ($stages as $stage) {
if ($stage->probability == 0.00) {
$data['lost'][] = $stage->crm_provider_id;
}
if ($stage->probability == 100.00) {
$data['won'][] = $stage->crm_provider_id;
}
}
$this->cachedClosedDealStages = $data;
return $data;
}
/**
* Import deals into the database with pre-fetched associations.
*
* API calls here (getAssociationsData, getExistingOpportunityCrmIds) are NOT
* caught — if they throw, the exception propagates to ImportOpportunityBatch::handle()
* where Laravel retries the whole job with backoff. After all retries exhausted,
* failed() requeues all IDs to Redis.
*
* The per-deal loop catches exceptions individually. A deal can end up in three states:
* - success: imported/updated successfully
* - failed_ids: exception thrown (DB constraint violation, corrupt data, etc.)
* These are permanent issues — retrying won't fix them.
* - skipped (null): missing dependencies (no account, unknown pipeline/stage).
* This is acceptable — the deal cannot be imported until those exist.
*/
private function importOpportunityBatch(array $deals): array
{
$syncedOpportunities = [
'success' => [],
'failed_ids' => [],
];
$dealIds = array_column($deals, 'id');
// Shared association/existing-ID preparation is batch-level state. If it fails, rethrow so the
// queue job retries the whole batch and eventually requeues all deal IDs back to Redis.
try {
$companyAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'companies');
$contactAssociations = $this->client->getAssociationsData($dealIds, 'deals', 'contacts');
$associationsData = $this->prepareAssociatedEntities($companyAssociations, $contactAssociations);
$existingCrmIds = $this->crmEntityRepository->getExistingOpportunityCrmIds(
$this->config,
array_map('strval', $dealIds)
);
$existingCrmIdSet = array_flip($existingCrmIds);
} catch (\Throwable $e) {
$this->logger->error('[' . $this->getDisplayName() . '] Failed to fetch associations or existing IDs', [
'teamId' => $this->team->getId(),
'dealCount' => count($dealIds),
'error' => $e->getMessage(),
]);
throw $e;
}
foreach ($deals as $deal) {
try {
$deal['associations'] = $this->prepareAssociationsForOpportunity(
$deal['id'],
$companyAssociations,
$contactAssociations,
$associationsData
);
$syncedOpportunity = $this->importOrUpdateOpportunity(
$deal,
isset($existingCrmIdSet[(string) $deal['id']])
);
if ($syncedOpportunity) {
$syncedOpportunities['success'][] = $syncedOpportunity;
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import opportunity', [
'teamId' => $this->team->getId(),
'crmId' => $deal['id'],
'error' => $e->getMessage(),
]);
$syncedOpportunities['failed_ids'][] = $deal['id'];
$syncedOpportunities['errors'][$deal['id']] = $e->getMessage();
}
}
return $syncedOpportunities;
}
/**
* Prepare associated entities for opportunities with optimized batch processing
* Returns structured data with CRM ID to DB ID mappings for each opportunity
*/
private function prepareAssociatedEntities(array $companyAssociations, array $contactAssociations): array
{
// Step 1: Collect all unique company and contact IDs from associations
$allCompanyIds = $this->flattenAssociationIds($companyAssociations);
$allContactIds = $this->flattenAssociationIds($contactAssociations);
// Step 2: Batch sync missing entities and get CRM ID to DB ID mappings
$companyIdMappings = [];
$contactIdMappings = [];
if (! empty($allCompanyIds)) {
$companyIdMappings = $this->prepareAssociatedAccounts($allCompanyIds);
}
if (! empty($allContactIds)) {
$contactIdMappings = $this->prepareAssociatedContacts($allContactIds);
}
return [
'company_id_mappings' => $companyIdMappings,
'contact_id_mappings' => $contactIdMappings,
];
}
/**
* Flatten association data to get unique IDs
*/
private function flattenAssociationIds(array $associations): array
{
$ids = [];
foreach ($associations as $dealAssociations) {
if (is_array($dealAssociations)) {
foreach ($dealAssociations as $id) {
$ids[$id] = true;
}
}
}
return array_keys($ids);
}
/**
* Batch sync missing accounts
*/
private function prepareAssociatedAccounts(array $companyIds): array
{
// Find which accounts already exist
$existingAccounts = $this->crmEntityRepository
->findAccountsByExternalIds($this->config, $companyIds);
$existingCompanyIds = $existingAccounts->pluck('crm_provider_id')->toArray();
$existingAccountsData = $existingAccounts->mapWithKeys(function ($account) {
return [$account->getCrmProviderId() => $account->getId()];
})->toArray();
$missingCompanyIds = array_diff($companyIds, $existingCompanyIds);
if (empty($missingCompanyIds)) {
return $existingAccountsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing accounts', [
'teamId' => $this->team->getUuid(),
'total_companies' => count($companyIds),
'existing_companies' => count($existingCompanyIds),
'missing_companies' => count($missingCompanyIds),
]);
// we already have limit on opportunity ids count
// Initialize variable before try block
$syncedAccountsData = [];
try {
$syncedAccountsData = $this->batchSyncCrmObjects('companies', $missingCompanyIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing accounts', [
'size' => count($missingCompanyIds),
'error' => $e->getMessage(),
]);
$syncedAccountsData = [];
}
return $existingAccountsData + $syncedAccountsData;
}
/**
* Prepare associated contacts - find existing and sync missing ones
* Returns mapping of CRM ID to DB ID
*/
private function prepareAssociatedContacts(array $contactIds): array
{
// Find which contacts already exist
$existingContacts = $this->crmEntityRepository
->findContactsByExternalIds($this->config, $contactIds);
$existingContactIds = $existingContacts->pluck('crm_provider_id')->toArray();
// Create mapping for existing contacts
$existingContactsData = $existingContacts->mapWithKeys(function ($contact) {
return [$contact->getCrmProviderId() => $contact->getId()];
})->toArray();
$missingContactIds = array_diff($contactIds, $existingContactIds);
if (empty($missingContactIds)) {
return $existingContactsData;
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch syncing missing contacts', [
'teamId' => $this->team->getUuid(),
'total_contacts' => count($contactIds),
'existing_contacts' => count($existingContactIds),
'missing_contacts' => count($missingContactIds),
]);
// Sync missing contacts using batch API
try {
$syncedContactsData = $this->batchSyncCrmObjects('contacts', $missingContactIds);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to sync missing contacts', [
'size' => count($missingContactIds),
'error' => $e->getMessage(),
]);
$syncedContactsData = [];
}
return $existingContactsData + $syncedContactsData;
}
private function batchSyncCrmObjects(string $objectType, array $crmIds): array
{
$syncObjects = [];
$crmObjectIds = array_values($crmIds);
foreach (array_chunk($crmObjectIds, self::BATCH_SIZE) as $chunk) {
try {
$objects = $objectType === 'companies' ?
$this->client->getCompaniesByIds($chunk, $this->getCompanyFields()) :
$this->client->getContactsByIds($chunk, $this->getContactFields());
foreach ($objects as $objectId => $objectData) {
$this->importCrmObject($objectType, (string) $objectId, $objectData, $syncObjects);
}
$this->logger->info('[' . $this->getDisplayName() . '] Batch synced ' . $objectType, [
'requested_count' => count($chunk),
'synced_count' => count($objects),
]);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Batch ' . $objectType . ' sync failed', [
'ids' => $chunk,
'error' => $e->getMessage(),
]);
}
}
return $syncObjects;
}
private function importCrmObject(string $objectType, string $objectId, mixed $objectData, array &$syncObjects): void
{
try {
$object = $objectType === 'companies' ?
$this->importAccount($objectData) :
$this->importContact($objectData);
if ($object) {
$syncObjects[$object->getCrmProviderId()] = $object->getId();
}
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to import batch ' . $objectType, [
'id' => $objectId,
'error' => $e->getMessage(),
]);
}
}
/**
* Prepare associations for a single opportunity
*
* The return value is an array with the following structure:
* [
* 'companies' => [
* $companyCrmId => $companyId,
* ...
* ],
* 'contacts' => [
* $contactCrmId => $contactId,
* ...
* ],
* 'account_id' => $accountId,
* ]
*/
private function prepareAssociationsForOpportunity(
string $oppCrmId,
array $companyAssociations,
array $contactAssociations,
array $associationsData
): array {
$associations = [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
$oppCompanyIds = $companyAssociations[$oppCrmId] ?? [];
foreach ($oppCompanyIds as $companyCrmId) {
if (isset($associationsData['company_id_mappings'][$companyCrmId])) {
$associations['companies'][$companyCrmId] = $associationsData['company_id_mappings'][$companyCrmId];
// Set primary account (first company becomes primary account)
if ($associations['account_id'] === null) {
$associations['account_id'] = $associationsData['company_id_mappings'][$companyCrmId];
}
}
}
$oppContactIds = $contactAssociations[$oppCrmId] ?? [];
foreach ($oppContactIds as $contactCrmId) {
if (isset($associationsData['contact_id_mappings'][$contactCrmId])) {
$associations['contacts'][$contactCrmId] = $associationsData['contact_id_mappings'][$contactCrmId];
}
}
return $associations;
}
/**
* Update only associations for an opportunity
*/
private function updateOpportunityAssociations(Opportunity $opportunity, array $associations): void
{
// Update contact associations
$this->importOpportunityContacts($opportunity, $associations['contacts']);
// Update company (account) associations
$this->updateOpportunityAccount($opportunity, $associations['account_id']);
}
/**
* Remove all contact associations from an opportunity
*/
private function removeAllOpportunityContacts(Opportunity $opportunity): void
{
$currentCount = (int) $opportunity->contacts()->count();
if ($currentCount > 0) {
$opportunity->contacts()->detach();
$this->logger->info('[' . $this->getDisplayName() . '] Removed all contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_count' => $currentCount,
]);
}
}
private function updateOpportunityAccount(Opportunity $opportunity, ?int $accountId): void
{
if ($accountId === null) {
// No account ID provided - keep current account
return;
}
$currentAccountId = $opportunity->getAccountId();
// Only update if account has changed
if ($currentAccountId !== $accountId) {
$opportunity->account_id = $accountId;
$opportunity->save();
$this->logger->info('[' . $this->getDisplayName() . '] Updated opportunity account association', [
'opportunity_id' => $opportunity->getId(),
'old_account_id' => $currentAccountId,
'new_account_id' => $accountId,
]);
}
}
/**
* Find existing opportunities by external IDs (OPTIMIZED VERSION)
* Uses batch query for better performance
*/
private function findExistingOpportunities(array $crmIds): Collection
{
return $this->crmEntityRepository
->findOpportunitiesByExternalIds($this->config, $crmIds);
}
private function processOpportunityBatch(array $opportunities): int
{
$syncedOpportunities = $this->importOpportunityBatch($opportunities);
return count($syncedOpportunities['success'] ?? []);
}
/**
* Convert single deal associations from HubSpot format to internal format
* Handles both HubSpot SDK objects and array formats
*
* @param array $opportunityAssociations Raw associations from HubSpot API or pre-processed
*
* @return array Processed associations with DB IDs
*/
private function convertDealAssociations(array $opportunityAssociations): array
{
$associations = $this->initializeAssociationsStructure();
if (empty($opportunityAssociations)) {
return $associations;
}
$associationIds = $this->extractAssociationIds($opportunityAssociations);
$this->processCompanyAssociations($associationIds, $associations);
$this->processContactAssociations($associationIds, $associations);
return $associations;
}
private function initializeAssociationsStructure(): array
{
return [
'companies' => [],
'contacts' => [],
'account_id' => null, // Primary account for opportunity
];
}
private function extractAssociationIds(array $opportunityAssociations): array
{
$associationIds = [];
foreach ($opportunityAssociations as $type => $associationData) {
if (! empty($associationData)) {
$associationIds[$type] = $this->convertSingleDealAssociations($associationData);
}
}
return $associationIds;
}
private function processCompanyAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['companies'])) {
return;
}
$companyId = $associationIds['companies'][0];
$account = $this->findOrSyncAccount($companyId);
if ($account instanceof Account) {
$associations['companies'][$companyId] = $account->getId();
$associations['account_id'] = $account->getId();
}
}
private function processContactAssociations(array $associationIds, array &$associations): void
{
if (empty($associationIds['contacts'])) {
return;
}
foreach ($associationIds['contacts'] as $contactId) {
$contact = $this->findOrSyncContact($contactId);
if ($contact instanceof Contact) {
$associations['contacts'][$contactId] = $contact->getId();
}
}
}
private function findOrSyncAccount(string $companyId): ?Account
{
$account = $this->crmEntityRepository->findAccountByExternalId($this->config, $companyId);
if (! $account instanceof Account) {
$account = $this->syncAccount($companyId);
}
return $account;
}
private function findOrSyncContact(string $contactId): ?Contact
{
$contact = $this->crmEntityRepository->findContactByExternalId($this->config, $contactId);
if (! $contact instanceof Contact) {
$contact = $this->syncContact($contactId);
}
return $contact;
}
private function convertSingleDealAssociations($opportunityAssociations = null): array
{
$associationData = [];
if ($opportunityAssociations === null) {
return $associationData;
}
// Handle array input (from extractAssociationIds)
if (is_array($opportunityAssociations)) {
return $opportunityAssociations;
}
// Handle CollectionResponseAssociatedId object
if ($opportunityAssociations instanceof CollectionResponseAssociatedId) {
foreach ($opportunityAssociations->getResults() as $association) {
$associationData[] = $association->getId();
}
}
return $associationData;
}
private function importOrUpdateOpportunity($crmData, ?bool $exists = null): ?Opportunity
{
if (empty($crmData['properties'])) {
return null;
}
$crmId = (string) $crmData['id'];
$properties = $crmData['properties'];
$associations = $crmData['associations'] ?? [];
$opportunityExists = $exists ?? (bool) $this->crmEntityRepository->findOpportunityByExternalId(
$this->config,
$crmId
);
if ($opportunityExists) {
return $this->updateOpportunity($crmId, $properties, $associations);
} else {
return $this->createOpportunity($crmId, $properties, $associations);
}
}
/**
* Create new opportunity
*/
private function createOpportunity(string $crmId, array $properties, array $associations): ?Opportunity
{
$accountId = $this->resolveAccountId($associations);
if (! $accountId) {
return null;
}
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
if (! $businessProcess) {
return null;
}
$stage = $this->resolveStage($businessProcess, $properties['dealstage'] ?? null);
if (! $stage) {
return null;
}
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->importOpportunityContacts($opportunity, $associations['contacts']);
if ($opportunity->wasRecentlyCreated) {
MatchActivitiesToNewOpportunity::dispatch($opportunity->getId());
}
return $opportunity;
}
/**
* Update existing opportunity
*/
private function updateOpportunity(string $crmId, array $properties, array $associations): Opportunity
{
$accountId = $this->resolveAccountId($associations);
$businessProcess = $this->resolveBusinessProcess($properties['pipeline'] ?? null);
$stage = $businessProcess ? $this->resolveStage($businessProcess, $properties['dealstage'] ?? null) : null;
$data = $this->buildOpportunityData($properties, $accountId, $businessProcess, $stage);
$attributes = [
'crm_configuration_id' => $this->config->getId(),
'crm_provider_id' => $crmId,
];
$values = array_merge($attributes, $data);
$opportunity = $this->crmEntityRepository->upsertOpportunity($attributes, $values);
$this->importExternalFieldData($properties, $opportunity->getId());
$this->updateOpportunityAssociations($opportunity, $associations);
return $opportunity;
}
private function resolveAccountId(array $associations): ?int
{
if (! empty($associations['accountId'])) {
return $associations['accountId'];
}
if (empty($associations)) {
return null;
}
// we can't resolve multiple account ids (currently SDK returns one company)
foreach ($associations['companies'] as $accountId) {
return $accountId;
}
return null;
}
private function buildOpportunityData(
array $properties,
?int $accountId,
?BusinessProcess $businessProcess,
?Stage $stage
): array {
$ownerId = null;
$profile = null;
if (! empty($properties['hubspot_owner_id'])) {
$ownerId = $properties['hubspot_owner_id'];
$profile = $this->crmEntityRepository->findProfileByExternalId($this->config, (string) $ownerId);
}
$name = 'Unknown';
if (isset($properties['dealname'])) {
$name = mb_strimwidth($properties['dealname'], 0, 128);
}
$amount = $this->resolveAmount($properties);
$currency = $properties['deal_currency_code'] ?? null;
$closeDate = null;
if (! empty($properties['closedate'])) {
$closeDate = Carbon::parse($properties['closedate'])->format('Y-m-d');
}
$remotelyCreatedAt = null;
if (! empty($properties['createdate']) && strtotime($properties['createdate'])) {
$date = $this->parseCleanDatetime($properties['createdate']);
$remotelyCreatedAt = $date?->format('Y-m-d H:i:s');
}
$closedStages = $this->getClosedDealStages();
$isWon = in_array($properties['dealstage'], $closedStages['won']);
$isLost = in_array($properties['dealstage'], $closedStages['lost']);
$data = [
'team_id' => $this->team->getId(),
'user_id' => $profile ? $profile->user_id : null,
'owner_id' => $ownerId,
'name' => $name,
'value' => ! empty($amount) ? $amount : null,
'currency_code' => CurrencyFormatter::formatCode($currency),
'close_date' => $closeDate,
'is_closed' => $isWon || $isLost,
'is_won' => $isWon,
'remotely_created_at' => $remotelyCreatedAt,
'probability' => $this->resolveDealProbability($properties['hs_deal_stage_probability']),
'forecast_category' => $this->resolveForecastCategory($properties['hs_manual_forecast_category']),
];
if ($accountId) {
$data['account_id'] = $accountId;
}
if ($stage) {
$data['stage_id'] = $stage->id;
}
if ($businessProcess) {
$recordType = $this->crmEntityRepository->getBusinessProcessRecordType($businessProcess);
if ($recordType) {
$data['record_type_id'] = $recordType->id;
}
}
return $data;
}
private function resolveBusinessProcess(?string $pipelineId): ?BusinessProcess
{
if ($pipelineId === null) {
return null;
}
if (isset($this->cachedBusinessProcesses[$pipelineId])) {
return $this->cachedBusinessProcesses[$pipelineId];
}
$businessProcess = $this->getBusinessProcess($pipelineId);
if (! $businessProcess instanceof BusinessProcess) {
$this->importStages();
$businessProcess = $this->getBusinessProcess($pipelineId);
}
if (! $businessProcess instanceof BusinessProcess) {
$this->logger->info(
'[HubSpot] Deal is not attached to a pipeline',
[
'pipeline' => $pipelineId]
);
}
$this->cachedBusinessProcesses[$pipelineId] = $businessProcess;
return $businessProcess;
}
private function getBusinessProcess(string $pipelineId): ?BusinessProcess
{
return $this->crmEntityRepository->findBusinessProcessesByExternalId($this->config, $pipelineId);
}
private function resolveStage(BusinessProcess $businessProcess, ?string $stageId): ?Stage
{
if (empty($stageId)) {
return null;
}
$cacheKey = $businessProcess->getId() . ':' . $stageId;
if (isset($this->cachedStages[$cacheKey])) {
return $this->cachedStages[$cacheKey];
}
$stage = $this->crmEntityRepository->getPipelineStageByConditions(
$businessProcess,
[
'crm_provider_id' => $stageId,
'type' => Stage::TYPE_OPPORTUNITY,
]
);
if ($stage === null) {
$this->importStages(null, $stageId);
}
if ($stage === null) {
$this->logger->info('[HubSpot] Stage does not exist => ' . $stageId);
}
$this->cachedStages[$cacheKey] = $stage;
return $stage;
}
private function resolveAmount(array $properties): ?string
{
$amount = null;
if (! empty($properties['amount'])) {
$amount = str_replace(',', '', $properties['amount']);
}
if ($this->config->hasDefaultCurrencyFieldSet()) {
$valueFieldName = $this->config->getDefaultCurrencyField()->getCrmProviderId();
$amount = $properties[$valueFieldName] ?? $amount;
}
return $amount;
}
private function parseCleanDatetime(string $datetime): ?Carbon
{
// Treat pre-1980 values as invalid
$minValidDate = Carbon::parse('1980-01-01 00:00:00');
try {
$date = Carbon::parse($datetime);
if ($minValidDate->gt($date)) {
return null;
}
return $date;
} catch (Exception) {
return null; // On parse error, treat as null
}
}
private function resolveDealProbability(?string $stageProbability): int
{
if ($stageProbability === null) {
return 0;
}
$probability = (float) $stageProbability;
return $probability > 1 ? 0 : (int) ($probability * 100);
}
private function resolveForecastCategory(?string $forecastCategory): string
{
if (! $forecastCategory) {
return Forecast::FORECAST_CATEGORY_UNCATEGORIZED;
}
$forecastCategory = str_replace('_', ' ', $forecastCategory);
return ucwords(strtolower($forecastCategory));
}
private function importExternalFieldData(array $properties, int $opportunityId): void
{
$crmFields = $this->getOpportunitySyncableFields();
$this->importOpportunityCrmFieldData($properties, $crmFields, $opportunityId);
}
private function importOpportunityContacts(Opportunity $opportunity, array $associations): void
{
// Handle empty or missing contact associations
if (empty($associations)) {
// Remove all existing contact associations if none provided
$this->removeAllOpportunityContacts($opportunity);
return;
}
// Use differential sync approach for better performance and accuracy
$this->syncOpportunityContactsDifferential($opportunity, $associations);
}
/**
* Sync opportunity contacts using differential approach
* This compares current vs new associations and only makes necessary changes
*/
private function syncOpportunityContactsDifferential(Opportunity $opportunity, array $contactAssociations): void
{
$currentContactCrmIds = $this->getCurrentContactCrmIds($opportunity);
$contactAssociationIds = array_keys($contactAssociations);
$contactsToAdd = array_diff($contactAssociationIds, $currentContactCrmIds);
$contactsToRemove = array_diff($currentContactCrmIds, $contactAssociationIds);
if (empty($contactsToAdd) && empty($contactsToRemove)) {
return;
}
$this->logContactAssociationChanges($opportunity, $currentContactCrmIds, $contactAssociations, $contactsToAdd, $contactsToRemove);
$this->removeContactAssociations($opportunity, $contactsToRemove);
$this->addContactAssociations($opportunity, $contactsToAdd, $contactAssociations);
}
private function getCurrentContactCrmIds(Opportunity $opportunity): array
{
return $opportunity->contacts()
->pluck('contacts.crm_provider_id')
->toArray();
}
private function logContactAssociationChanges(
Opportunity $opportunity,
array $currentContactCrmIds,
array $contactAssociations,
array $contactsToAdd,
array $contactsToRemove
): void {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association changes', [
'opportunity_id' => $opportunity->getId(),
'current_contacts' => $currentContactCrmIds,
'new_contacts' => $contactAssociations,
'contacts_to_add' => $contactsToAdd,
'contacts_to_remove' => $contactsToRemove,
]);
}
private function removeContactAssociations(Opportunity $opportunity, array $contactsToRemove): void
{
if (empty($contactsToRemove)) {
return;
}
$contactsToDetach = $opportunity->contacts()
->whereIn('contacts.crm_provider_id', $contactsToRemove)
->pluck('contacts.id')
->toArray();
if (! empty($contactsToDetach)) {
$opportunity->contacts()->detach($contactsToDetach);
$this->logger->info('[' . $this->getDisplayName() . '] Removed contact associations', [
'opportunity_id' => $opportunity->getId(),
'removed_contact_crm_ids' => $contactsToRemove,
'removed_contact_count' => count($contactsToDetach),
]);
}
}
private function addContactAssociations(Opportunity $opportunity, array $contactsToAdd, array $contactAssociations): void
{
if (empty($contactsToAdd)) {
return;
}
$contactsAdded = [];
foreach ($contactsToAdd as $crmId) {
$id = $contactAssociations[$crmId];
if ($this->attachSingleContact($opportunity, (string) $crmId, $id)) {
$contactsAdded[] = $crmId;
}
}
$this->logAddedContacts($opportunity, $contactsAdded);
}
private function attachSingleContact(Opportunity $opportunity, string $crmId, int $id): bool
{
try {
$contact = $this->crmEntityRepository->findContactByConfigurationAndId($this->config, $id);
if (! $contact) {
return false;
}
return $this->performContactAttachment($opportunity, $contact, $crmId);
} catch (\Throwable $e) {
$this->logger->warning('[' . $this->getDisplayName() . '] Failed to add contact association', [
'opportunity_id' => $opportunity->getId(),
'contact_crm_id' => $crmId,
'error' => $e->getMessage(),
]);
return false;
}
}
private function performContactAttachment(Opportunity $opportunity, Contact $contact, string $crmId): bool
{
try {
$opportunity->contacts()->attach($contact->getId(), [
'crm_provider_id' => $crmId,
]);
return true;
} catch (\Illuminate\Database\QueryException $e) {
if (str_contains($e->getMessage(), 'Duplicate entry')) {
$this->logger->info('[' . $this->getDisplayName() . '] Contact association already exists', [
'contact_id' => $contact->getId(),
'contact_crm_id' => $crmId,
'opportunity_id' => $opportunity->getId(),
]);
return false;
}
throw $e;
}
}
private function logAddedContacts(Opportunity $opportunity, array $contactsAdded): void
{
if (! empty($contactsAdded)) {
$this->logger->info('[' . $this->getDisplayName() . '] Added contact associations', [
'opportunity_id' => $opportunity->getId(),
'contacts_to_add_count' => count($contactsAdded),
'added_contact_crm_ids' => $contactsAdded,
'added_contacts_count' => count($contactsAdded),
]);
}
}
}...
|
PhpStorm
|
faVsco.js – crm_configurations [PROD]
|
NULL
|
48262
|
|
67990
|
PhostormINavigarecodeLaravelKeractorTOOISWindowFV PhostormINavigarecodeLaravelKeractorTOOISWindowFV faVsco.js( #11894 on JY-18909-automated-reports-askProledey<> phpunit.xmlTa raw sal_query.sal© AskJiminnyReportActivityService.phpM+ READMc.mo2o3 sonar-project.properties= test.pv‹ Untitled Diagram.xmlusvetur.confia.s) AutomatedkeporisCallbackservice.pngpnp api.ohopip apivz.ohpM+ WEBHOOK FILTERING IMPLEMENTATIC•1h External Uorariesv E° Scratches and Consolesv Database ConsolesV AEUA console fEUlADEALRISKS TEUISDIlSUIAEU [EU)v A jiminny@localhostA console (jiminny@localhost]A DI fjiminny@localhost]A HS_local [jiminny@localhost]A SF [jiminny@localhost]A zoho_dev (jiminny@localhost]V A PRODA console [PROD1A console 1 [PROD]& DI PRODSTAAServicesv M DatabasevAEUconsole 2 s 799 msv&jiminny@localhostA SFA HS_localV A PROD4 console 1 s 749 msfii crm configurations 2 s 115 msV A STAGING# console 1s 35/ ms& DockercLass asaomatedkeportsservzcelest extends lesttaseprivate function getService(Smockuserkeposicory = null.SmockTeamRepos1tory = null.): AutomatedReportsService {...}112 V#DataProvider'transformMed1alvoesDataProv1der')public function testTransformMediaTypes(array $mediaTypes, array $expected): void{...}public function testGetMediaTypeFieldDataWithoutReport: voidt...134 Vpublic function testGetMediaTypeFieldDataWithReport: voidt...}lusagepublic static function transformMediaTypesDataProvider(): arrayf…….}#[DataProvider('hasCallTypeConferenceDataProvider')]public function testHasCallTypeConference(array $callTypes, bool Sexpected): void{...}#[DataProvider('hasCallTvoeDialerDataProvider')][2026-04-21 19:25:39] Connected[2026-04-21 19:25:40] jiminny> use jiminny2026-04-21 19:25:400completed in 137 ms[2026-04-21 19:25:40] jiminny> SELECT t.*FRor 1aminnv.crm contiqurations tWHERE id = 537LIME 501(2026-04-21 19:25:411 1 row retrieved starting from 1 in 738 ms (execution: 181 ms. fetching: 557 ms)100% LzTue 21 Apr 19:26:01tt crm_configurations [PROD] *(c) AutomatedRenortsRenositorvTest.ohn© Field.php(©) FieldRepository.pnp© JiminnyDebugCommand.php© AutomatedReportsSendCommand.phpC)AutomatedPenortcCommand.nhnC) AutomatedReportsservice.onp© TrackProviderInstalledEvent.php© SendReportMailJob.php© RequestGenerateAskJiminnyReportJobTest.phpRequestGeneratereportJob.pnp© AutomatedReportResult.phpc)Automatedreport.pnom410 492 ×69 ^ V Q621•762- 624=625626-628-420=630-632- 633-634635=custom.log=laravel.log4 SF jiminny@localhost]HS_local [jiminny@localhost]console PROD)A console [EU]A console [STAGING]Tx: Autovt.id AS team_idtnameFROM teams tJOIN users u 1<->1..n: ON u.team_id = t.idLOWER (SUBSTRING_INDEX(c.calendar_provider_id, '@', -1)) AS calendar_domainLEFT JOIN team_domains tdON td.team_id = t.idAND td.deleted_at IS NULLAND td.domain = LOWER(SUBSTRING_INDEX(c.calendar_provider id, '@'. -1))GROUP BY t.id, t.name, calendar domainORDER BY t.name,calendar domainselect * from users u join calendars c 1<->1..n: on c.user id = u.idwhere u.team id = 882:select * from activities where id = 58081273; # team 537select * from participants where activity_id = 58081273:dojiminny035 A1 A33 V62 ^W Windsurf Teams 635:20 UTF-8Aenasod...
|
PhpStorm
|
faVsco.js – crm_configurations [PROD]
|
NULL
|
67990
|
|
67991
|
iTerm2ShellEditViewSessionScriptsProfilesWindowHel iTerm2ShellEditViewSessionScriptsProfilesWindowHelp₴81-zsh₴2-zshDOCKER883docker_lamp_1docker_1amp_1docker_1amp_1• '/usr/local/bin/php' 'artisan'mailbox: text-relay:sync > '/proc/1/fdocker_lamp_12026-04-21 16:25:14 Running ['artisan'conference: pre-meeting-notificdocker_1amp_1'/proc/1/fd/1'docker_lamp_11 '/usr/local/bin/php' 'artisan'conference:pre-meeting-notification2026-04-21 16:25:18 Running ['artisan'conference:monitor:start]1 '/usr/local/bin/php' 'artisan' conference:monitor:start › '/proc/1/docker_1amp_12026-04-21 16:25:24 Running ['artisan'conference:monitor:end]docker_lamp_11 '/usr/local/bin/php' 'artisan' conference:monitor:end › */proc/1/fddocker_lamp_1 | 2026-04-21 16:25:31 Running ['artisan' jiminny:fix-hubspot-tokens]docker_lamp_11 • '/usr/local/bin/php' 'artisan' jiminny:fix-hubspot-tokens > */proc/docker_lamp_1|2026-04-21 16:25:37 Running ['artisan' conference:pre-meeting-reminder] in background 2.59ms DONE• ('/usr/local/bin/php' 'artisan' conference:pre-meeting-reminder › '/proc/1/fd/1' 2>&1 ; '/usr/local/bin/php' 'artisan'schedule:finish "framework/schedule-805efb160ee8d9da02e60364ace7970eb2b35f31" "$?") › '/dev/null' 2>&1 &docker_1amp_12026-04-21 16:25:37 Running ['artisan' hubspot: journal-poll --start]in background1.04ms DONEdocker_1amp_1• ('/usr/local/bin/php' 'artisan' hubspot: journal-poll --start › '/proc/1/fd/1' 2>&1 ; '/usr/local/bin/php'schedule:finish "framework/schedule-e26d77f915d2c55fe91ca4148a230e32eaa1865e" "$?") > '/dev/null' 2>&1 &docker_lamp_12026-04-21 16:25:37 Running ['artisan' crm:bullhorn:ping --heartbeat]0 social account(s) to be processeddocker_1amp_1docker_lamp_1docker_lamp_1docker_lamp_11 Done!1 2 Starting HubSpot journal polling service...4S DONEdocker_1amp_11 '/usr/local/bin/php' 'artisan' crm:bullhorn:ping --heartbeat › */proc/1/fd/1' 2>&1docker_lamp_1docker_lamp_1run_artisan_schedule: Done waiting for schedule:runView in Docker Desktop• View ConfigEnable WatchEU (ssh)* Build full day activity...• 84screenpipe"• ₴512PROD (ssh)Run 'do-release-upgrade' to upgrade to it.-zsh*** System restart required ***Last login: Mon Apr 20 15:14:15 2026 from 212.5.153.87lukas@jiminny-prod-bastion:~$ UX T3 EU (ssh)New release '24.04.4 LTS' available.Run 'do-release-upgrade' to upgrade to it.*** System restart required ***Last login: Tue Apr 21 06:17:16 2026 from 212.5.153.87lukas@jiminny-eu-bastion:~$T4 STAGE (ssh)New release '24.04.4 LTS' available.Run 'do-release-upgrade' to upgrade to it.Last login: Thu Apr 16 07:34:39 2026 from [IP_ADDRESS]:-$T5 QA (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentsX T6 FE (-zsh)Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentslukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $ IX Y7 EXT (-zsh)Last login: Mon Apr 20 19:48:04 on ttys005Poetry could not find a pyproject.toml file in /Users/lukas or its parentsPoetry could not find a pyproject.toml file in /Users/lukas or its parentsukas@Lukas-Kovaliks-MacBook-Pro-Jiminny ~ $100% <78Tue 21 Apr 19:26:05APP (-zsh)181PRODSTAGEFRONTENDEXTENSION...
|
PhpStorm
|
faVsco.js – crm_configurations [PROD]
|
NULL
|
67991
|
|
67992
|
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
AutomatedReportsServiceTest
Run 'AutomatedReportsServiceTest'
Debug 'AutomatedReportsServiceTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
10
92
69
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Tests\Unit\Services\Kiosk\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Support\Carbon as IlluminateCarbon;
use Illuminate\Contracts\Bus\Dispatcher;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Jiminny\Component\AskAnything\AskAnythingPromptService;
use Jiminny\Component\AskAnything\Dtos\AskAnythingPromptDto;
use Jiminny\Component\UrlGenerator\Webhook;
use Jiminny\Contracts\Repositories\PlaybookCategoryRepository;
use Jiminny\Contracts\Repositories\TeamRepository;
use Jiminny\Contracts\Repositories\UserRepository;
use Jiminny\Exceptions\InvalidArgumentException;
use Jiminny\Exceptions\ModelNotFoundException;
use Illuminate\Support\Collection;
use Jiminny\Models\AskAnything\AskAnythingPrompt;
use Jiminny\Models\AskAnything\AskAnythingPromptTarget;
use Jiminny\Models\Activity\Search;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Feature\FeatureEnum;
use Jiminny\Models\Group;
use Jiminny\Models\Team;
use Jiminny\Models\User;
use Jiminny\Repositories\AskAnythingRepository;
use Jiminny\Repositories\AutomatedReportsRepository;
use Jiminny\Repositories\GroupRepository;
use Jiminny\Repositories\SearchRepository;
use Jiminny\Repositories\StageRepository;
use Jiminny\Services\Kiosk\AutomatedReports\ActivityTypeService;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Jiminny\Services\Kiosk\AutomatedReports\DealStagesService;
use Jiminny\Services\Kiosk\AutomatedReports\RecipientsService;
use Mockery;
use PHPUnit\Framework\Attributes\DataProvider;
use Tests\TestCase;
class AutomatedReportsServiceTest extends TestCase
{
private AutomatedReportsService $service;
protected function setUp(): void
{
parent::setUp();
// Create a real instance of the service without calling the constructor
$reflection = new \ReflectionClass(AutomatedReportsService::class);
$this->service = $reflection->newInstanceWithoutConstructor();
// Manually set the dependencies using reflection
$dependencies = [
'teamRepository' => TeamRepository::class,
'groupRepository' => GroupRepository::class,
'userRepository' => UserRepository::class,
'stageRepository' => StageRepository::class,
'dealStagesService' => DealStagesService::class,
'recipientsService' => RecipientsService::class,
'automatedReportsRepository' => AutomatedReportsRepository::class,
'webhookService' => Webhook::class,
'dispatcher' => Dispatcher::class,
'activityTypeService' => ActivityTypeService::class,
'playbookCategoryRepository' => PlaybookCategoryRepository::class,
'askAnythingPromptService' => AskAnythingPromptService::class,
'activitySearchRepository' => SearchRepository::class,
'askAnythingRepository' => AskAnythingRepository::class,
];
foreach ($dependencies as $propertyName => $class) {
$property = $reflection->getProperty($propertyName);
$property->setAccessible(true);
$property->setValue($this->service, $this->createMock($class));
}
}
protected function tearDown(): void
{
parent::tearDown();
Mockery::close();
}
private function getService(
$mockUserRepository = null,
$mockStageRepository = null,
$mockTeamRepository = null,
): AutomatedReportsService {
return new AutomatedReportsService(
($mockTeamRepository ?? $this->createMock(TeamRepository::class)),
$this->createMock(GroupRepository::class),
($mockUserRepository ?? $this->createMock(UserRepository::class)),
($mockStageRepository ?? $this->createMock(StageRepository::class)),
$this->createMock(DealStagesService::class),
$this->createMock(RecipientsService::class),
$this->createMock(AutomatedReportsRepository::class),
$this->createMock(Webhook::class),
$this->createMock(Dispatcher::class),
$this->createMock(ActivityTypeService::class),
$this->createMock(PlaybookCategoryRepository::class),
$this->createMock(AskAnythingPromptService::class),
$this->createMock(SearchRepository::class),
$this->createMock(AskAnythingRepository::class),
);
}
#[DataProvider('transformMediaTypesDataProvider')]
public function testTransformMediaTypes(array $mediaTypes, array $expected): void
{
$report = new AutomatedReport(['media_types' => $mediaTypes]);
$reflection = new \ReflectionClass(AutomatedReportsService::class);
$method = $reflection->getMethod('transformMediaTypes');
$result = $method->invoke($this->service, $report);
$this->assertEquals($expected, $result);
}
public function testGetMediaTypeFieldDataWithoutReport(): void
{
$result = $this->service->getMediaTypeFieldData(null);
$this->assertIsArray($result);
$this->assertArrayHasKey('value', $result);
$this->assertEmpty($result['value']);
$this->assertEquals('media_types', $result['id']);
}
public function testGetMediaTypeFieldDataWithReport(): void
{
$mediaTypes = ['pdf', 'podcast'];
$report = new AutomatedReport(['media_types' => $mediaTypes]);
$result = $this->service->getMediaTypeFieldData($report);
$expectedValue = [
['id' => 'pdf', 'name' => 'PDF'],
['id' => 'podcast', 'name' => 'Podcast'],
];
$this->assertIsArray($result);
$this->assertArrayHasKey('value', $result);
$this->assertEquals($expectedValue, $result['value']);
}
public static function transformMediaTypesDataProvider(): array
{
return [
'empty array' => [
'mediaTypes' => [],
'expected' => [],
],
'pdf only' => [
'mediaTypes' => ['pdf'],
'expected' => [
['id' => 'pdf', 'name' => 'PDF'],
],
],
'podcast only' => [
'mediaTypes' => ['podcast'],
'expected' => [
['id' => 'podcast', 'name' => 'Podcast'],
],
],
'both pdf and podcast' => [
'mediaTypes' => ['pdf', 'podcast'],
'expected' => [
['id' => 'pdf', 'name' => 'PDF'],
['id' => 'podcast', 'name' => 'Podcast'],
],
],
'with invalid type' => [
'mediaTypes' => ['pdf', 'invalid', 'podcast'],
'expected' => [
['id' => 'pdf', 'name' => 'PDF'],
['id' => 'podcast', 'name' => 'Podcast'],
],
],
];
}
#[DataProvider('hasCallTypeConferenceDataProvider')]
public function testHasCallTypeConference(array $callTypes, bool $expected): void
{
$report = $this->createMock(AutomatedReport::class);
$report->method('getCallTypes')->willReturn($callTypes);
$result = $this->service->hasCallTypeConference($report);
$this->assertEquals($expected, $result);
}
#[DataProvider('hasCallTypeDialerDataProvider')]
public function testHasCallTypeDialer(array $callTypes, bool $expected): void
{
$report = $this->createMock(AutomatedReport::class);
$report->method('getCallTypes')->willReturn($callTypes);
$result = $this->service->hasCallTypeDialer($report);
$this->assertEquals($expected, $result);
}
public static function hasCallTypeConferenceDataProvider(): array
{
return [
'has conference' => [
'callTypes' => ['conference', 'dialer'],
'expected' => true,
],
'does not have conference' => [
'callTypes' => ['dialer', 'other'],
'expected' => false,
],
'empty call types' => [
'callTypes' => [],
'expected' => false,
],
];
}
public static function hasCallTypeDialerDataProvider(): array
{
return [
'has dialer' => [
'callTypes' => ['conference', 'dialer'],
'expected' => true,
],
'does not have dialer' => [
'callTypes' => ['conference', 'other'],
'expected' => false,
],
'empty call types' => [
'callTypes' => [],
'expected' => false,
],
];
}
public function testTransformReportResultsWithEmptyCollection(): void
{
$emptyCollection = new Collection([]);
$result = $this->service->transformReportResults($emptyCollection);
$this->assertIsArray($result);
$this->assertEmpty($result);
}
public function testTransformReportResultsStructure(): void
{
// Create a mock AutomatedReportResult with minimal setup to test structure
$mockReportResult = $this->createMockReportResult();
$collection = new Collection([$mockReportResult]);
$result = $this->service->transformReportResults($collection);
$this->assertIsArray($result);
$this->assertCount(1, $result);
$transformedResult = $result[0];
// Verify all expected keys are present
$expectedKeys = [
'id', 'name', 'frequency', 'recipients',
'report_type', 'media_type', 'downloadUrl', 'viewUrl', 'generated_at',
];
foreach ($expectedKeys as $key) {
$this->assertArrayHasKey($key, $transformedResult);
}
// Verify structure of nested arrays
$this->assertIsArray($transformedResult['frequency']);
$this->assertArrayHasKey('id', $transformedResult['frequency']);
$this->assertArrayHasKey('name', $transformedResult['frequency']);
$this->assertIsArray($transformedResult['report_type']);
$this->assertArrayHasKey('id', $transformedResult['report_type']);
$this->assertArrayHasKey('name', $transformedResult['report_type']);
$this->assertIsArray($transformedResult['recipients']);
// Verify TODO fields are null as expected
$this->assertEquals(AutomatedReportsService::MEDIA_TYPE_PODCAST, $transformedResult['media_type']);
$this->assertEquals(route('ai-reports.audio.download', ['uuid' => 'test-uuid']), $transformedResult['downloadUrl']);
$this->assertEquals(route('ai-reports.audio.view', ['uuid' => 'test-uuid']), $transformedResult['viewUrl']);
}
public function testTransformReportResultsWithMultipleResults(): void
{
$mockReportResult1 = $this->createMockReportResult('result-uuid-1', 'exec_summary');
$mockReportResult2 = $this->createMockReportResult('result-uuid-2', 'coaching_profiles');
$collection = new Collection([$mockReportResult1, $mockReportResult2]);
$result = $this->service->transformReportResults($collection);
$this->assertIsArray($result);
$this->assertCount(2, $result);
// Verify different UUIDs
$this->assertEquals('result-uuid-1', $result[0]['id']);
$this->assertEquals('result-uuid-2', $result[1]['id']);
// Verify both results have the expected structure
foreach ($result as $transformedResult) {
$this->assertArrayHasKey('id', $transformedResult);
$this->assertArrayHasKey('name', $transformedResult);
$this->assertArrayHasKey('frequency', $transformedResult);
$this->assertArrayHasKey('recipients', $transformedResult);
$this->assertArrayHasKey('report_type', $transformedResult);
}
}
#[DataProvider('isUserRecipientOfReportDataProvider')]
public function testIsUserRecipientOfReport(int $userId, array $recipients, bool $expected): void
{
// Create mock User
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn($userId);
$mockUser->method('getGroupId')->willReturn(null);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn($recipients);
$mockReport->method('isAskJiminnyReport')->willReturn(false);
$mockReport->method('getGroups')->willReturn([]);
$result = $this->service->isUserRecipientOfReport($mockUser, $mockReport);
$this->assertEquals($expected, $result);
}
#[DataProvider('isUserRecipientOfAskJiminnyReportDataProvider')]
public function testIsUserRecipientOfAskJiminnyReportViaGroup(
int $userId,
?int $groupId,
array $recipients,
array $reportGroups,
bool $expected,
): void {
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn($userId);
$mockUser->method('getGroupId')->willReturn($groupId);
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn($recipients);
$mockReport->method('isAskJiminnyReport')->willReturn(true);
$mockReport->method('getGroups')->willReturn($reportGroups);
$this->assertSame($expected, $this->service->isUserRecipientOfReport($mockUser, $mockReport));
}
public function testIsUserRecipientOfNonAskJiminnyReportIgnoresGroups(): void
{
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn(123);
$mockUser->method('getGroupId')->willReturn(5);
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['users' => []]);
$mockReport->method('isAskJiminnyReport')->willReturn(false);
$mockReport->method('getGroups')->willReturn([5]);
$this->assertFalse($this->service->isUserRecipientOfReport($mockUser, $mockReport));
}
public static function isUserRecipientOfAskJiminnyReportDataProvider(): array
{
return [
'group member - ask jiminny' => [
'userId' => 123,
'groupId' => 7,
'recipients' => ['users' => []],
'reportGroups' => [7],
'expected' => true,
],
'group mismatch - ask jiminny' => [
'userId' => 123,
'groupId' => 9,
'recipients' => ['users' => []],
'reportGroups' => [7, 8],
'expected' => false,
],
'user with no group - ask jiminny' => [
'userId' => 123,
'groupId' => null,
'recipients' => ['users' => []],
'reportGroups' => [7],
'expected' => false,
],
'recipient users take precedence over group' => [
'userId' => 123,
'groupId' => null,
'recipients' => ['users' => [123]],
'reportGroups' => [],
'expected' => true,
],
];
}
public function testIsUserRecipientOfReportWithEmptyRecipients(): void
{
// Create mock User
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn(123);
// Create mock AutomatedReport with no recipients
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn([]);
$result = $this->service->isUserRecipientOfReport($mockUser, $mockReport);
$this->assertFalse($result);
}
public function testIsUserRecipientOfReportWithNoUsersKey(): void
{
// Create mock User
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getId')->willReturn(123);
// Create mock AutomatedReport with recipients but no 'users' key
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['other_key' => [456, 789]]);
$result = $this->service->isUserRecipientOfReport($mockUser, $mockReport);
$this->assertFalse($result);
}
public static function isUserRecipientOfReportDataProvider(): array
{
return [
'user is recipient - single user' => [
'userId' => 123,
'recipients' => ['users' => [123]],
'expected' => true,
],
'user is recipient - multiple users' => [
'userId' => 456,
'recipients' => ['users' => [123, 456, 789]],
'expected' => true,
],
'user is not recipient - single user' => [
'userId' => 999,
'recipients' => ['users' => [123]],
'expected' => false,
],
'user is not recipient - multiple users' => [
'userId' => 999,
'recipients' => ['users' => [123, 456, 789]],
'expected' => false,
],
'user is recipient - string IDs converted to int' => [
'userId' => 123,
'recipients' => ['users' => ['123', '456']],
'expected' => true,
],
'user is not recipient - string IDs converted to int' => [
'userId' => 999,
'recipients' => ['users' => ['123', '456']],
'expected' => false,
],
'empty users array' => [
'userId' => 123,
'recipients' => ['users' => []],
'expected' => false,
],
];
}
private function createMockReportResult(string $uuid = 'test-uuid', string $reportType = 'exec_summary'): AutomatedReportResult
{
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getFrequency')->willReturn('weekly');
$mockReport->method('getRecipients')->willReturn(['users' => [1, 2]]);
$mockReport->method('getGroups')->willReturn([10, 20]);
$mockReport->method('getType')->willReturn($reportType);
// Create mock Team
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
// Create mock Group
$mockGroup = $this->createMock(\Jiminny\Models\Group::class);
$mockGroup->method('getUuid')->willReturn('group-uuid-10');
$mockGroup->method('getName')->willReturn('Test Team');
$mockQueryBuilder = Mockery::mock();
$mockQueryBuilder->shouldReceive('where')->andReturnSelf();
$mockQueryBuilder->shouldReceive('first')->andReturn($mockGroup);
$dataRelation = Mockery::mock(HasMany::class);
$dataRelation->shouldReceive('where')->andReturn($mockQueryBuilder);
$dataRelation->shouldReceive('get')->andReturn(
new \Illuminate\Database\Eloquent\Collection([$mockGroup])
);
$mockTeam->method('groups')->willReturn($dataRelation);
$mockReport->method('getTeam')->willReturn($mockTeam);
// Create mock AutomatedReportResult
$mockReportResult = $this->createMock(AutomatedReportResult::class);
$mockReportResult->method('getUuid')->willReturn($uuid);
$mockReportResult->method('getGeneratedAt')->willReturn(
\Illuminate\Support\Carbon::parse('2024-01-15T10:30:00Z')
);
$mockReportResult->method('getReport')->willReturn($mockReport);
// Mock methods used in getReportFileName
$mockReportResult->method('getReportType')->willReturn($reportType);
$mockReportResult->method('getFromDate')->willReturn(
\Illuminate\Support\Carbon::parse('2024-01-08')
);
$mockReportResult->method('getToDate')->willReturn(
\Illuminate\Support\Carbon::parse('2024-01-15')
);
$mockReportResult->method('getGroups')->willReturn([10]);
$mockReportResult->method('getMediaType')->willReturn(AutomatedReportsService::MEDIA_TYPE_PODCAST);
return $mockReportResult;
}
#[DataProvider('getUsersUuidsDataProvider')]
public function testGetUsersUuids(array $recipients, array $mockUsers, array $expectedUuids): void
{
// Create mock UserRepository
$mockUserRepository = $this->createMock(UserRepository::class);
// Configure the mock to return specific users for specific IDs using a callback
$mockUserRepository->method('find')
->willReturnCallback(function ($userId) use ($mockUsers) {
if (! isset($mockUsers[$userId])) {
return null;
}
$userUuid = $mockUsers[$userId]['uuid'] ?? null;
if ($userUuid === null) {
return null;
}
$mockUser = $this->createMock(\Jiminny\Models\User::class);
$mockUser->method('getUuid')->willReturn((string) $userUuid);
return $mockUser;
});
// Create service with mocked UserRepository
$automatedReportsService = $this->getService(mockUserRepository: $mockUserRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn($recipients);
$result = $automatedReportsService->getUsersUuids($mockReport);
$this->assertEquals($expectedUuids, $result);
}
public function testGetUsersUuidsWithEmptyRecipients(): void
{
// Create mock AutomatedReport with empty recipients
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn([]);
$result = $this->service->getUsersUuids($mockReport);
$this->assertEquals([], $result);
}
public function testGetUsersUuidsWithNoUsersKey(): void
{
// Create mock AutomatedReport with recipients but no 'users' key
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['other_key' => [1, 2, 3]]);
$result = $this->service->getUsersUuids($mockReport);
$this->assertEquals([], $result);
}
public function testGetUsersUuidsWithNonExistentUsers(): void
{
// Create mock UserRepository that returns null for all users
$mockUserRepository = $this->createMock(UserRepository::class);
$mockUserRepository->method('find')->willReturn(null);
// Create service with mocked UserRepository
$automatedReportsService = $this->getService(mockUserRepository: $mockUserRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getRecipients')->willReturn(['users' => [1, 2, 3]]);
$result = $automatedReportsService->getUsersUuids($mockReport);
// Should return array with null values for non-existent users
$this->assertEquals([], $result);
}
public static function getUsersUuidsDataProvider(): array
{
return [
'single user found' => [
'recipients' => ['users' => [123]],
'mockUsers' => [
123 => ['id' => 123, 'uuid' => 'user-uuid-123'],
],
'expectedUuids' => ['user-uuid-123'],
],
'multiple users found' => [
'recipients' => ['users' => [123, 456, 789]],
'mockUsers' => [
123 => ['id' => 123, 'uuid' => 'user-uuid-123'],
456 => ['id' => 456, 'uuid' => 'user-uuid-456'],
789 => ['id' => 789, 'uuid' => 'user-uuid-789'],
],
'expectedUuids' => ['user-uuid-123', 'user-uuid-456', 'user-uuid-789'],
],
'mixed found and not found users' => [
'recipients' => ['users' => [123, 456, 789]],
'mockUsers' => [
123 => ['id' => 123, 'uuid' => 'user-uuid-123'],
// 456 not found in DB
789 => ['id' => 789, 'uuid' => 'user-uuid-789'],
],
'expectedUuids' => ['user-uuid-123', 'user-uuid-789'], // Updated to reflect that nulls are filtered out
],
'empty users array' => [
'recipients' => ['users' => []],
'mockUsers' => [],
'expectedUuids' => [],
],
'all users not found' => [
'recipients' => ['users' => [123, 456]],
'mockUsers' => [], // No users found
'expectedUuids' => [], // Updated to reflect that nulls are filtered out
],
];
}
#[DataProvider('getCurrentDealStagesUuidsDataProvider')]
public function testGetCurrentDealStagesUuids(array $currentDealStages, array $mockStages, array $expectedUuids): void
{
// Create mock StageRepository
$mockStageRepository = $this->createMock(StageRepository::class);
// Configure the mock to return specific stages for specific IDs using a callback
$mockStageRepository->method('find')
->willReturnCallback(function ($stageId) use ($mockStages) {
if (! isset($mockStages[$stageId])) {
return null;
}
$stageUuid = $mockStages[$stageId]['uuid'] ?? null;
if ($stageUuid === null) {
return null;
}
$mockStage = $this->createMock(\Jiminny\Models\Stage::class);
$mockStage->method('getUuid')->willReturn((string) $stageUuid);
return $mockStage;
});
// Create service with mocked StageRepository
$automatedReportsService = $this->getService(mockStageRepository: $mockStageRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getCurrentDealStages')->willReturn($currentDealStages);
$result = $automatedReportsService->getCurrentDealStagesUuids($mockReport);
$this->assertEquals($expectedUuids, $result);
}
public function testGetCurrentDealStagesUuidsWithEmptyStages(): void
{
// Create mock AutomatedReport with empty current deal stages
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getCurrentDealStages')->willReturn([]);
$result = $this->service->getCurrentDealStagesUuids($mockReport);
$this->assertEquals([], $result);
}
public function testGetCurrentDealStagesUuidsWithNonExistentStages(): void
{
// Create mock StageRepository that returns null for all stages
$mockStageRepository = $this->createMock(StageRepository::class);
$mockStageRepository->method('find')->willReturn(null);
// Create service with mocked StageRepository
$automatedReportsService = $this->getService(mockStageRepository: $mockStageRepository);
// Create mock AutomatedReport
$mockReport = $this->createMock(AutomatedReport::class);
$mockReport->method('getCurrentDealStages')->willReturn([1, 2, 3]);
$result = $automatedReportsService->getCurrentDealStagesUuids($mockReport);
// Should return array with null values for non-existent stages
$this->assertEquals([], $result);
}
public static function getCurrentDealStagesUuidsDataProvider(): array
{
return [
'single stage found' => [
'currentDealStages' => [10],
'mockStages' => [
10 => ['id' => 10, 'uuid' => 'stage-uuid-10'],
],
'expectedUuids' => ['stage-uuid-10'],
],
'multiple stages found' => [
'currentDealStages' => [10, 20, 30],
'mockStages' => [
10 => ['id' => 10, 'uuid' => 'stage-uuid-10'],
20 => ['id' => 20, 'uuid' => 'stage-uuid-20'],
30 => ['id' => 30, 'uuid' => 'stage-uuid-30'],
],
'expectedUuids' => ['stage-uuid-10', 'stage-uuid-20', 'stage-uuid-30'],
],
'mixed found and not found stages' => [
'currentDealStages' => [10, 20, 30],
'mockStages' => [
10 => ['id' => 10, 'uuid' => 'stage-uuid-10'],
// 20 not found in DB
30 => ['id' => 30, 'uuid' => 'stage-uuid-30'],
],
'expectedUuids' => ['stage-uuid-10', 'stage-uuid-30'], // Updated to reflect that nulls are filtered out
],
'empty stages array' => [
'currentDealStages' => [],
'mockStages' => [],
'expectedUuids' => [],
],
'all stages not found' => [
'currentDealStages' => [10, 20],
'mockStages' => [], // No stages found
'expectedUuids' => [], // Updated to reflect that nulls are filtered out
],
];
}
#[DataProvider('getTeamGroupsDataProvider')]
public function testGetTeamGroups(string $teamUuid, ?array $mockTeamData, array $mockGroups, array $expectedResult): void
{
// Create mock TeamRepository
$mockTeamRepository = $this->createMock(TeamRepository::class);
if ($mockTeamData === null) {
// Team not found
$mockTeamRepository->method('idOrUuid')
->with($teamUuid)
->willReturn(null);
} else {
// Team found - create mock team with groups
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
// Create mock groups collection
$mockGroupsCollection = $this->createMock(\Illuminate\Database\Eloquent\Collection::class);
// Create mock Group objects
$groupObjects = [];
foreach ($mockGroups as $groupData) {
$mockGroup = $this->createMock(\Jiminny\Models\Group::class);
$mockGroup->method('getUuid')->willReturn($groupData['id']);
$mockGroup->method('getName')->willReturn($groupData['name']);
$groupObjects[] = $mockGroup;
}
// Mock the groups collection to return our mock groups
$mockGroupsCollection->method('getIterator')->willReturn(new \ArrayIterator($groupObjects));
// Mock the groups() relation
$mockGroupsRelation = $this->createMock(\Illuminate\Database\Eloquent\Relations\HasMany::class);
$mockGroupsRelation->method('get')->willReturn($mockGroupsCollection);
$mockTeam->method('groups')->willReturn($mockGroupsRelation);
$mockTeamRepository->method('idOrUuid')
->with($teamUuid)
->willReturn($mockTeam);
}
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeamGroups($teamUuid);
$this->assertEquals($expectedResult, $result);
}
public function testGetTeamGroupsWithNonExistentTeam(): void
{
// Create mock TeamRepository that returns null (team not found)
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('idOrUuid')->willReturn(null);
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeamGroups('non-existent-team-uuid');
$this->assertEquals([], $result);
}
public function testGetTeamGroupsWithEmptyGroups(): void
{
// Create mock team with no groups
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
// Create empty groups collection
$mockGroupsCollection = $this->createMock(\Illuminate\Database\Eloquent\Collection::class);
$mockGroupsCollection->method('getIterator')->willReturn(new \ArrayIterator([]));
$mockGroupsRelation = $this->createMock(\Illuminate\Database\Eloquent\Relations\HasMany::class);
$mockGroupsRelation->method('get')->willReturn($mockGroupsCollection);
$mockTeam->method('groups')->willReturn($mockGroupsRelation);
// Create mock TeamRepository
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('idOrUuid')->willReturn($mockTeam);
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeamGroups('team-with-no-groups');
$this->assertEquals([], $result);
}
public static function getTeamGroupsDataProvider(): array
{
return [
'team with single group' => [
'teamUuid' => 'team-uuid-123',
'mockTeamData' => ['id' => 'team-uuid-123', 'name' => 'Test Team'],
'mockGroups' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
],
'expectedResult' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
],
],
'team with multiple groups' => [
'teamUuid' => 'team-uuid-456',
'mockTeamData' => ['id' => 'team-uuid-456', 'name' => 'Another Team'],
'mockGroups' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
['id' => 'group-uuid-2', 'name' => 'Marketing Team'],
['id' => 'group-uuid-3', 'name' => 'Support Team'],
],
'expectedResult' => [
['id' => 'group-uuid-1', 'name' => 'Sales Team'],
['id' => 'group-uuid-2', 'name' => 'Marketing Team'],
['id' => 'group-uuid-3', 'name' => 'Support Team'],
],
],
'team not found' => [
'teamUuid' => 'non-existent-uuid',
'mockTeamData' => null,
'mockGroups' => [],
'expectedResult' => [],
],
'team with no groups' => [
'teamUuid' => 'team-uuid-empty',
'mockTeamData' => ['id' => 'team-uuid-empty', 'name' => 'Empty Team'],
'mockGroups' => [],
'expectedResult' => [],
],
];
}
#[DataProvider('getTeamsDataProvider')]
public function testGetTeams(array $mockTeams, array $expectedResult): void
{
// Create mock TeamRepository
$mockTeamRepository = $this->createMock(TeamRepository::class);
// Create mock Team objects
$teamObjects = [];
foreach ($mockTeams as $teamData) {
$mockTeam = $this->createMock(\Jiminny\Models\Team::class);
$mockTeam->method('getUuid')->willReturn($teamData['id']);
$mockTeam->method('getName')->willReturn($teamData['name']);
$mockTeam->method('hasFeature')
->with(\Jiminny\Models\Feature\FeatureEnum::AUTOMATED_REPORTS)
->willReturn($teamData['hasAutomatedReports']);
$teamObjects[] = $mockTeam;
}
// Mock the repository to return a Collection (not array)
$mockTeamRepository->method('getTeamsForKiosk')
->with('active')
->willReturn(new Collection($teamObjects));
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeams();
$this->assertEquals($expectedResult, $result);
}
public function testGetTeamsWithNoTeams(): void
{
// Create mock TeamRepository that returns empty Collection
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('getTeamsForKiosk')->willReturn(new Collection([]));
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeams();
$this->assertEquals([], $result);
}
public function testGetTeamsWithAllTeamsWithoutFeature(): void
{
// Create mock teams without AUTOMATED_REPORTS feature
$mockTeam1 = $this->createMock(\Jiminny\Models\Team::class);
$mockTeam1->method('hasFeature')
->with(\Jiminny\Models\Feature\FeatureEnum::AUTOMATED_REPORTS)
->willReturn(false);
$mockTeam2 = $this->createMock(\Jiminny\Models\Team::class);
$mockTeam2->method('hasFeature')
->with(\Jiminny\Models\Feature\FeatureEnum::AUTOMATED_REPORTS)
->willReturn(false);
// Create mock TeamRepository that returns Collection
$mockTeamRepository = $this->createMock(TeamRepository::class);
$mockTeamRepository->method('getTeamsForKiosk')->willReturn(new Collection([$mockTeam1, $mockTeam2]));
// Create service with mocked TeamRepository
$automatedReportsService = $this->getService(mockTeamRepository: $mockTeamRepository);
$result = $automatedReportsService->getTeams();
$this->assertEquals([], $result);
}
public static function getTeamsDataProvider(): array
{
return [
'single team with feature' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => true,
],
],
'expectedResult' => [
['id' => 'team-uuid-1', 'name' => 'Sales Team'],
],
],
'multiple teams with feature' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => true,
],
[
'id' => 'team-uuid-2',
'name' => 'Marketing Team',
'hasAutomatedReports' => true,
],
[
'id' => 'team-uuid-3',
'name' => 'Support Team',
'hasAutomatedReports' => true,
],
],
'expectedResult' => [
['id' => 'team-uuid-1', 'name' => 'Sales Team'],
['id' => 'team-uuid-2', 'name' => 'Marketing Team'],
['id' => 'team-uuid-3', 'name' => 'Support Team'],
],
],
'mixed teams - some with feature, some without' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => true,
],
[
'id' => 'team-uuid-2',
'name' => 'Marketing Team',
'hasAutomatedReports' => false,
],
[
'id' => 'team-uuid-3',
'name' => 'Support Team',
'hasAutomatedReports' => true,
],
],
'expectedResult' => [
['id' => 'team-uuid-1', 'name' => 'Sales Team'],
['id' => 'team-uuid-3', 'name' => 'Support Team'],
],
],
'all teams without feature' => [
'mockTeams' => [
[
'id' => 'team-uuid-1',
'name' => 'Sales Team',
'hasAutomatedReports' => false,
],
[
'id' => 'team-uuid-2',
'name' => 'Marketing Team',
'hasAutomatedReports' => false,
],
],
'expectedResult' => [],
],
'empty teams array' => [
'mockTeams' => [],
'expectedResult' => [],
],
];
}
#[DataProvider('deleteS3FilesDataProvider')]
public function testDeleteS3Files(
string $mediaType,
array $expectedFileExtensions,
array $existingFiles,
string $pathSuffix,
int $expectedDeletes
): void {
// Arrange
$teamUuid = 'team-uuid-123';
$reportUuid = 'report-uuid-456';
$basePath = sprintf('%s/reports/%s', $teamUuid, $reportUuid);
$team = Mockery::mock(Team::class);
$team->allows('getUuid')->andReturn($teamUuid);
$report = Mockery::mock(AutomatedReport::class);
$report->allows('getTeam')->andReturn($team);
$result = Mockery::mock(AutomatedReportResult::class);
$result->allows('getReport')->andReturn($report);
$result->allows('getUuid')->andReturn($reportUuid);
$result->allows('getMediaType')->andReturn($mediaType);
Storage::fake();
Log::shouldReceive('info')->times($expectedDeletes);
foreach ($existingFiles as $extension) {
$filePath = $basePath . $pathSuffix . '.' . $extension;
Storage::put($filePath, 'dummy content');
}
// Act
$this->service->deleteS3Files($result);
// Assert
foreach ($expectedFileExtensions as $extension) {
$filePath = $basePath . $pathSuffix . '.' . $extension;
if (in_array($extension, $existingFiles, true)) {
Storage::assertMissing($filePath);
} else {
// To be sure no unexpected files were created and deleted
Storage::assertMissing($filePath);
}
}
}
public static function deleteS3FilesDataProvider(): array
{
return [
'PDF report, all files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PDF,
'expectedFileExtensions' => ['html', 'MD', 'pdf'],
'existingFiles' => ['html', 'MD', 'pdf'],
'pathSuffix' => '',
'expectedDeletes' => 3,
],
'PDF report, some files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PDF,
'expectedFileExtensions' => ['html', 'MD', 'pdf'],
'existingFiles' => ['html', 'pdf'],
'pathSuffix' => '',
'expectedDeletes' => 2,
],
'PDF report, no files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PDF,
'expectedFileExtensions' => ['html', 'MD', 'pdf'],
'existingFiles' => [],
'pathSuffix' => '',
'expectedDeletes' => 0,
],
'Podcast report, all files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'expectedFileExtensions' => ['json', 'mp3', 'ssml'],
'existingFiles' => ['json', 'mp3', 'ssml'],
'pathSuffix' => '_podcast',
'expectedDeletes' => 3,
],
'Podcast report, some files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'expectedFileExtensions' => ['json', 'mp3', 'ssml'],
'existingFiles' => ['mp3'],
'pathSuffix' => '_podcast',
'expectedDeletes' => 1,
],
'Podcast report, no files exist' => [
'mediaType' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'expectedFileExtensions' => ['json', 'mp3', 'ssml'],
'existingFiles' => [],
'pathSuffix' => '_podcast',
'expectedDeletes' => 0,
],
'Other media type, should do nothing' => [
'mediaType' => 'some_other_type',
'expectedFileExtensions' => [],
'existingFiles' => [],
'pathSuffix' => '',
'expectedDeletes' => 0,
],
];
}
public function testDeleteReportsResultsInRetentionPeriodWithLogging(): void
{
// Create mocks for the test
$automatedReportsService = Mockery::mock(AutomatedReportsService::class);
$team = Mockery::mock(Team::class);
$team->shouldReceive('getId')->andReturn(123);
$from = now()->subDays(30);
$to = now();
$source = 'test-source';
// Expect the method to be called with specific parameters
$automatedReportsService->shouldReceive('deleteReportsResultsInRetentionPeriodWithLogging')
->once()
->with(
$team,
Mockery::on(function ($arg) use ($from) {
return $arg->timestamp === $from->timestamp;
}),
Mockery::on(function ($arg) use ($to) {
return $arg->timestamp === $to->timestamp;
}),
$source
)
->andReturn(5);
// Call the method and verify the result
$result = $automatedReportsService->deleteReportsResultsInRetentionPeriodWithLogging(
$team,
$from,
$to,
$source
);
$this->assertEquals(5, $result);
}
#[DataProvider('sanitizeFileNameDataProvider')]
public function testSanitizeFileName(string $input, string $expected): void
{
$result = $this->service->sanitizeFileName($input);
$this->assertEquals($expected, $result);
}
public static function sanitizeFileNameDataProvider(): array
{
return [
'no special characters' => [
'input' => 'Exec Summary - Sep 2025 - Business Development Team',
'expected' => 'Exec Summary - Sep 2025 - Business Development Team',
],
'forward slash in team name' => [
'input' => 'Exec Summary - Sep 2025 - ND/IRV',
'expected' => 'Exec Summary - Sep 2025 - ND-IRV',
],
'backslash in team name' => [
'input' => 'Exec Summary - Sep 2025 - ND\IRV',
'expected' => 'Exec Summary - Sep 2025 - ND-IRV',
],
'multiple forward slashes' => [
'input' => 'Report - Team A/B/C',
'expected' => 'Report - Team A-B-C',
],
'multiple backslashes' => [
'input' => 'Report - Team A\B\C',
'expected' => 'Report - Team A-B-C',
],
'mixed slashes and backslashes' => [
'input' => 'Report - Team A/B\C',
'expected' => 'Report - Team A-B-C',
],
'complex team name with slashes' => [
'input' => 'Exec Summary - Sep 2025 - Business Development Team - ND/IRV, Net Driven - Acquisition (Sales)',
'expected' => 'Exec Summary - Sep 2025 - Business Development Team - ND-IRV, Net Driven - Acquisition (Sales)',
],
'only slashes' => [
'input' => '//\\\\',
'expected' => '----',
],
'empty string' => [
'input' => '',
'expected' => '',
],
'slash at start' => [
'input' => '/Report Name',
'expected' => '-Report Name',
],
'slash at end' => [
'input' => 'Report Name/',
'expected' => 'Report Name-',
],
];
}
public function testGetReportFileNameSanitizesOutput(): void
{
// Create mock GroupRepository
$mockGroupRepository = $this->createMock(GroupRepository::class);
// Create mock Group with slash in name
$mockGroup = $this->createMock(\Jiminny\Models\Group::class);
$mockGroup->method('getName')->willReturn('ND/IRV, Net Driven - Acquisition (Sales)');
$mockGroupRepository->method('find')->willReturn($mockGroup);
// Create service with mocked GroupRepository
$service = new AutomatedReportsService(
$this->c...
|
PhpStorm
|
faVsco.js – crm_configurations [PROD]
|
NULL
|
67992
|
|
42632
|
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
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\ApiResponseException;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
/**
* Log prefix for all log messages from this job
*/
private const string LOG_PREFIX = '[Report:Generate]';
private const int MIN_ACTIVITIES_COUNT = 10;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public int $tries = 2;
private readonly string $reportUuid;
private ?AutomatedReportResult $reportResult = null;
private ?AutomatedReportResult $reportResultPodcast = null;
public function __construct(string $reportUuid)
{
$this->reportUuid = $reportUuid;
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
ProphetClient $prophetClient,
LoggerInterface $logger
): void {
$logger->info(self::LOG_PREFIX . ' - Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport(uuid: $this->reportUuid);
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$this->createResults(automatedReport: $automatedReport, reportService: $reportService);
$payload = $reportService->getGenerateReportPayload(
automatedReport: $automatedReport,
reportResultUuid: $this->reportResult->getUuid()
);
$now = Carbon::now();
$this->reportResult->update([
'payload' => $payload,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'payload' => $payload,
'requested_at' => $now,
]);
}
if (! $this->checkActivityCount($prophetClient, $payload, $logger)) {
return;
}
$now = Carbon::now();
// send generate report request
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'name' => $reportService->getReportFileName($this->reportResultPodcast),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
}
$logger->info(self::LOG_PREFIX . ' - Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT,
requestArray: $payload
);
$logger->info(self::LOG_PREFIX . ' - Response received', ['response' => $response->getContent()]);
} catch (Throwable $exception) {
$reportUuid = null;
$reason = $exception instanceof ProphetException ?
AutomatedReportResult::REASON_PROPHET_API_ERROR : AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(
self::LOG_PREFIX . ' - Error',
[
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $reportUuid,
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]
);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' - Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' - Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' - Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function createResults(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService
): void {
$mediaTypes = $automatedReport->getMediaTypes();
// handle PDF or podcast
if (count($mediaTypes) === 1) {
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => $mediaTypes[0],
]
);
return;
}
// handle multiple media types
// create PDF as primary result
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
if (in_array(AutomatedReportsService::MEDIA_TYPE_PODCAST, $mediaTypes, true)) {
$this->reportResultPodcast = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'parent_id' => $this->reportResult->getId(),
]
);
}
}
private function checkActivityCount(ProphetClient $prophetClient, array $payload, LoggerInterface $logger): bool
{
$logger->info(self::LOG_PREFIX . ' - Request activities count', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
// validate expected activities count before sending request
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT_COUNT,
requestArray: $payload
);
$content = $response->getContent();
if (! isset($content['response'])) {
throw new ApiResponseException('Error getting activities count');
}
$logger->info(self::LOG_PREFIX . ' - Get activities count', $content);
$count = (int) $content['response'];
if ($count < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' - Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
if (isset($this->reportResult)) {
$this->reportResult->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
}
}
367
367
0
fa0cf643-d30e-43b8-ab3d-1fe18254609c
fa0cf643-d30e-43b8-ab3d-1fe18254609c
2026-04-17 07:14:23
459
459
0
<null>
<null>
0
hubspot
hubspot
2026-04-17 07:14:23
<null>
<null>
2026-04-17 07:14:23
<null>
<null>
2026-04-17 07:14:23
0
0
0
2
2
2026-04-17 07:14:23
<null>
<null>
2026-04-17 07:14:23
1
1
0
1...
|
PhpStorm
|
faVsco.js – crm_configurations [EU]
|
NULL
|
42632
|
|
42633
|
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
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\ApiResponseException;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
/**
* Log prefix for all log messages from this job
*/
private const string LOG_PREFIX = '[Report:Generate]';
private const int MIN_ACTIVITIES_COUNT = 10;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public int $tries = 2;
private readonly string $reportUuid;
private ?AutomatedReportResult $reportResult = null;
private ?AutomatedReportResult $reportResultPodcast = null;
public function __construct(string $reportUuid)
{
$this->reportUuid = $reportUuid;
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
ProphetClient $prophetClient,
LoggerInterface $logger
): void {
$logger->info(self::LOG_PREFIX . ' - Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport(uuid: $this->reportUuid);
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$this->createResults(automatedReport: $automatedReport, reportService: $reportService);
$payload = $reportService->getGenerateReportPayload(
automatedReport: $automatedReport,
reportResultUuid: $this->reportResult->getUuid()
);
$now = Carbon::now();
$this->reportResult->update([
'payload' => $payload,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'payload' => $payload,
'requested_at' => $now,
]);
}
if (! $this->checkActivityCount($prophetClient, $payload, $logger)) {
return;
}
$now = Carbon::now();
// send generate report request
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'name' => $reportService->getReportFileName($this->reportResultPodcast),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
}
$logger->info(self::LOG_PREFIX . ' - Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT,
requestArray: $payload
);
$logger->info(self::LOG_PREFIX . ' - Response received', ['response' => $response->getContent()]);
} catch (Throwable $exception) {
$reportUuid = null;
$reason = $exception instanceof ProphetException ?
AutomatedReportResult::REASON_PROPHET_API_ERROR : AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(
self::LOG_PREFIX . ' - Error',
[
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $reportUuid,
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]
);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' - Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' - Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' - Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function createResults(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService
): void {
$mediaTypes = $automatedReport->getMediaTypes();
// handle PDF or podcast
if (count($mediaTypes) === 1) {
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => $mediaTypes[0],
]
);
return;
}
// handle multiple media types
// create PDF as primary result
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
if (in_array(AutomatedReportsService::MEDIA_TYPE_PODCAST, $mediaTypes, true)) {
$this->reportResultPodcast = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'parent_id' => $this->reportResult->getId(),
]
);
}
}
private function checkActivityCount(ProphetClient $prophetClient, array $payload, LoggerInterface $logger): bool
{
$logger->info(self::LOG_PREFIX . ' - Request activities count', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
// validate expected activities count before sending request
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT_COUNT,
requestArray: $payload
);
$content = $response->getContent();
if (! isset($content['response'])) {
throw new ApiResponseException('Error getting activities count');
}
$logger->info(self::LOG_PREFIX . ' - Get activities count', $content);
$count = (int) $content['response'];
if ($count < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' - Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
if (isset($this->reportResult)) {
$this->reportResult->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
}
}
367
367
0
fa0cf643-d30e-43b8-ab3d-1fe18254609c
fa0cf643-d30e-43b8-ab3d-1fe18254609c
2026-04-17 07:14:23
459
459
0
<null>
<null>
0
hubspot
hubspot
2026-04-17 07:14:23
<null>
<null>
2026-04-17 07:14:23
<null>
<null>
2026-04-17 07:14:23
0
0
0
2
2
2026-04-17 07:14:23
<null>
<null>
2026-04-17 07:14:23
1
1
0
1
1
0...
|
PhpStorm
|
faVsco.js – crm_configurations [EU]
|
NULL
|
42633
|
|
42649
|
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
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\ApiResponseException;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
/**
* Log prefix for all log messages from this job
*/
private const string LOG_PREFIX = '[Report:Generate]';
private const int MIN_ACTIVITIES_COUNT = 10;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public int $tries = 2;
private readonly string $reportUuid;
private ?AutomatedReportResult $reportResult = null;
private ?AutomatedReportResult $reportResultPodcast = null;
public function __construct(string $reportUuid)
{
$this->reportUuid = $reportUuid;
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
ProphetClient $prophetClient,
LoggerInterface $logger
): void {
$logger->info(self::LOG_PREFIX . ' - Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport(uuid: $this->reportUuid);
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$this->createResults(automatedReport: $automatedReport, reportService: $reportService);
$payload = $reportService->getGenerateReportPayload(
automatedReport: $automatedReport,
reportResultUuid: $this->reportResult->getUuid()
);
$now = Carbon::now();
$this->reportResult->update([
'payload' => $payload,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'payload' => $payload,
'requested_at' => $now,
]);
}
if (! $this->checkActivityCount($prophetClient, $payload, $logger)) {
return;
}
$now = Carbon::now();
// send generate report request
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'name' => $reportService->getReportFileName($this->reportResultPodcast),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
}
$logger->info(self::LOG_PREFIX . ' - Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT,
requestArray: $payload
);
$logger->info(self::LOG_PREFIX . ' - Response received', ['response' => $response->getContent()]);
} catch (Throwable $exception) {
$reportUuid = null;
$reason = $exception instanceof ProphetException ?
AutomatedReportResult::REASON_PROPHET_API_ERROR : AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(
self::LOG_PREFIX . ' - Error',
[
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $reportUuid,
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]
);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' - Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' - Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' - Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function createResults(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService
): void {
$mediaTypes = $automatedReport->getMediaTypes();
// handle PDF or podcast
if (count($mediaTypes) === 1) {
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => $mediaTypes[0],
]
);
return;
}
// handle multiple media types
// create PDF as primary result
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
if (in_array(AutomatedReportsService::MEDIA_TYPE_PODCAST, $mediaTypes, true)) {
$this->reportResultPodcast = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'parent_id' => $this->reportResult->getId(),
]
);
}
}
private function checkActivityCount(ProphetClient $prophetClient, array $payload, LoggerInterface $logger): bool
{
$logger->info(self::LOG_PREFIX . ' - Request activities count', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
// validate expected activities count before sending request
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT_COUNT,
requestArray: $payload
);
$content = $response->getContent();
if (! isset($content['response'])) {
throw new ApiResponseException('Error getting activities count');
}
$logger->info(self::LOG_PREFIX . ' - Get activities count', $content);
$count = (int) $content['response'];
if ($count < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' - Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
if (isset($this->reportResult)) {
$this->reportResult->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
}
}
367
367
0
fa0cf643-d30e-43b8-ab3d-1fe18254609c
fa0cf643-d30e-43b8-ab3d-1fe18254609c
2026-04-17 07:14:23
459...
|
PhpStorm
|
faVsco.js – crm_configurations [EU]
|
NULL
|
42649
|
|
42650
|
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
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\ApiResponseException;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
/**
* Log prefix for all log messages from this job
*/
private const string LOG_PREFIX = '[Report:Generate]';
private const int MIN_ACTIVITIES_COUNT = 10;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public int $tries = 2;
private readonly string $reportUuid;
private ?AutomatedReportResult $reportResult = null;
private ?AutomatedReportResult $reportResultPodcast = null;
public function __construct(string $reportUuid)
{
$this->reportUuid = $reportUuid;
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
ProphetClient $prophetClient,
LoggerInterface $logger
): void {
$logger->info(self::LOG_PREFIX . ' - Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport(uuid: $this->reportUuid);
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$this->createResults(automatedReport: $automatedReport, reportService: $reportService);
$payload = $reportService->getGenerateReportPayload(
automatedReport: $automatedReport,
reportResultUuid: $this->reportResult->getUuid()
);
$now = Carbon::now();
$this->reportResult->update([
'payload' => $payload,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'payload' => $payload,
'requested_at' => $now,
]);
}
if (! $this->checkActivityCount($prophetClient, $payload, $logger)) {
return;
}
$now = Carbon::now();
// send generate report request
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'name' => $reportService->getReportFileName($this->reportResultPodcast),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
}
$logger->info(self::LOG_PREFIX . ' - Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT,
requestArray: $payload
);
$logger->info(self::LOG_PREFIX . ' - Response received', ['response' => $response->getContent()]);
} catch (Throwable $exception) {
$reportUuid = null;
$reason = $exception instanceof ProphetException ?
AutomatedReportResult::REASON_PROPHET_API_ERROR : AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(
self::LOG_PREFIX . ' - Error',
[
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $reportUuid,
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]
);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' - Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' - Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' - Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function createResults(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService
): void {
$mediaTypes = $automatedReport->getMediaTypes();
// handle PDF or podcast
if (count($mediaTypes) === 1) {
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => $mediaTypes[0],
]
);
return;
}
// handle multiple media types
// create PDF as primary result
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
if (in_array(AutomatedReportsService::MEDIA_TYPE_PODCAST, $mediaTypes, true)) {
$this->reportResultPodcast = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'parent_id' => $this->reportResult->getId(),
]
);
}
}
private function checkActivityCount(ProphetClient $prophetClient, array $payload, LoggerInterface $logger): bool
{
$logger->info(self::LOG_PREFIX . ' - Request activities count', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
// validate expected activities count before sending request
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT_COUNT,
requestArray: $payload
);
$content = $response->getContent();
if (! isset($content['response'])) {
throw new ApiResponseException('Error getting activities count');
}
$logger->info(self::LOG_PREFIX . ' - Get activities count', $content);
$count = (int) $content['response'];
if ($count < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' - Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
if (isset($this->reportResult)) {
$this->reportResult->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
}
}
367
367
0
fa0cf643-d30e-43b8-ab3d-1fe18254609c
fa0cf643-d30e-43b8-ab3d-1fe18254609c
2026-04-17 07:14:23
459
459...
|
PhpStorm
|
faVsco.js – crm_configurations [EU]
|
NULL
|
42650
|
|
42716
|
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
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\ApiResponseException;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
/**
* Log prefix for all log messages from this job
*/
private const string LOG_PREFIX = '[Report:Generate]';
private const int MIN_ACTIVITIES_COUNT = 10;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public int $tries = 2;
private readonly string $reportUuid;
private ?AutomatedReportResult $reportResult = null;
private ?AutomatedReportResult $reportResultPodcast = null;
public function __construct(string $reportUuid)
{
$this->reportUuid = $reportUuid;
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
ProphetClient $prophetClient,
LoggerInterface $logger
): void {
$logger->info(self::LOG_PREFIX . ' - Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport(uuid: $this->reportUuid);
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$this->createResults(automatedReport: $automatedReport, reportService: $reportService);
$payload = $reportService->getGenerateReportPayload(
automatedReport: $automatedReport,
reportResultUuid: $this->reportResult->getUuid()
);
$now = Carbon::now();
$this->reportResult->update([
'payload' => $payload,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'payload' => $payload,
'requested_at' => $now,
]);
}
if (! $this->checkActivityCount($prophetClient, $payload, $logger)) {
return;
}
$now = Carbon::now();
// send generate report request
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'name' => $reportService->getReportFileName($this->reportResultPodcast),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
}
$logger->info(self::LOG_PREFIX . ' - Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT,
requestArray: $payload
);
$logger->info(self::LOG_PREFIX . ' - Response received', ['response' => $response->getContent()]);
} catch (Throwable $exception) {
$reportUuid = null;
$reason = $exception instanceof ProphetException ?
AutomatedReportResult::REASON_PROPHET_API_ERROR : AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(
self::LOG_PREFIX . ' - Error',
[
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $reportUuid,
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]
);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' - Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' - Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' - Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function createResults(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService
): void {
$mediaTypes = $automatedReport->getMediaTypes();
// handle PDF or podcast
if (count($mediaTypes) === 1) {
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => $mediaTypes[0],
]
);
return;
}
// handle multiple media types
// create PDF as primary result
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
if (in_array(AutomatedReportsService::MEDIA_TYPE_PODCAST, $mediaTypes, true)) {
$this->reportResultPodcast = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'parent_id' => $this->reportResult->getId(),
]
);
}
}
private function checkActivityCount(ProphetClient $prophetClient, array $payload, LoggerInterface $logger): bool
{
$logger->info(self::LOG_PREFIX . ' - Request activities count', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
// validate expected activities count before sending request
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT_COUNT,
requestArray: $payload
);
$content = $response->getContent();
if (! isset($content['response'])) {
throw new ApiResponseException('Error getting activities count');
}
$logger->info(self::LOG_PREFIX . ' - Get activities count', $content);
$count = (int) $content['response'];
if ($count < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' - Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
if (isset($this->reportResult)) {
$this->reportResult->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
}
}
367
367
0
fa0cf643-d30e-43b8-ab3d-1fe18254609c
fa0cf643-d30e-43b8-ab3d-1fe18254609c
2026-04-17 07:14:23
459
459
0
<null>
<null>...
|
PhpStorm
|
faVsco.js – crm_configurations [EU]
|
NULL
|
42716
|
|
42717
|
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
AutomatedReportsCommandTest
Run 'AutomatedReportsCommandTest'
Debug 'AutomatedReportsCommandTest'
More Actions
JetBrains AI
Search Everywhere
IDE and Project Settings
Sync Changes
Hide This Notification
Code changed:
Hide
2
Previous Highlighted Error
Next Highlighted Error
<?php
declare(strict_types=1);
namespace Jiminny\Jobs\AutomatedReports;
use Carbon\Carbon;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Jiminny\Component\ProphetAi\Exceptions\ProphetException;
use Jiminny\Component\ProphetAi\ProphetClient;
use Jiminny\Component\Queue\Constants;
use Jiminny\Exceptions\ApiResponseException;
use Jiminny\Models\AutomatedReport;
use Jiminny\Models\AutomatedReportResult;
use Jiminny\Models\Team;
use Jiminny\Services\Kiosk\AutomatedReports\AutomatedReportsService;
use Psr\Log\LoggerInterface;
use Throwable;
class RequestGenerateReportJob implements ShouldQueue, ShouldBeUnique
{
use InteractsWithQueue;
use Queueable;
/**
* Log prefix for all log messages from this job
*/
private const string LOG_PREFIX = '[Report:Generate]';
private const int MIN_ACTIVITIES_COUNT = 10;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public int $tries = 2;
private readonly string $reportUuid;
private ?AutomatedReportResult $reportResult = null;
private ?AutomatedReportResult $reportResultPodcast = null;
public function __construct(string $reportUuid)
{
$this->reportUuid = $reportUuid;
$this->onQueue(Constants::QUEUE_ANALYTICS);
}
public function uniqueId(): string
{
return $this->reportUuid;
}
public function handle(
AutomatedReportsService $reportService,
ProphetClient $prophetClient,
LoggerInterface $logger
): void {
$logger->info(self::LOG_PREFIX . ' - Started', [
'automatedReportUuid' => $this->reportUuid,
]);
try {
$automatedReport = $reportService->getReport(uuid: $this->reportUuid);
if (! $this->validateReport($automatedReport, $logger)) {
return;
}
$this->createResults(automatedReport: $automatedReport, reportService: $reportService);
$payload = $reportService->getGenerateReportPayload(
automatedReport: $automatedReport,
reportResultUuid: $this->reportResult->getUuid()
);
$now = Carbon::now();
$this->reportResult->update([
'payload' => $payload,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'payload' => $payload,
'requested_at' => $now,
]);
}
if (! $this->checkActivityCount($prophetClient, $payload, $logger)) {
return;
}
$now = Carbon::now();
// send generate report request
$this->reportResult->update([
'name' => $reportService->getReportFileName($this->reportResult),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'name' => $reportService->getReportFileName($this->reportResultPodcast),
'status' => AutomatedReportResult::STATUS_REQUESTED,
'requested_at' => $now,
]);
}
$logger->info(self::LOG_PREFIX . ' - Request sent', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT,
requestArray: $payload
);
$logger->info(self::LOG_PREFIX . ' - Response received', ['response' => $response->getContent()]);
} catch (Throwable $exception) {
$reportUuid = null;
$reason = $exception instanceof ProphetException ?
AutomatedReportResult::REASON_PROPHET_API_ERROR : AutomatedReportResult::REASON_DEFAULT;
$this->failReport($reason);
$logger->error(
self::LOG_PREFIX . ' - Error',
[
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $reportUuid,
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
]
);
if ($this->attempts() < $this->tries) {
$logger->info(self::LOG_PREFIX . ' - Retry scheduled', [
'attempts' => $this->attempts(),
]);
$this->release(30);
} else {
$this->fail($exception);
}
}
}
private function validateReport(AutomatedReport $automatedReport, LoggerInterface $logger): bool
{
if (! $automatedReport->getStatus()) {
$logger->info(self::LOG_PREFIX . ' - Skipped, report is not active', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
if ($automatedReport->getTeam()->getStatus() !== Team::STATUS_ACTIVE) {
$logger->info(self::LOG_PREFIX . ' - Skipped, team is inactive', [
'automatedReportUuid' => $this->reportUuid,
]);
return false;
}
return true;
}
private function createResults(
AutomatedReport $automatedReport,
AutomatedReportsService $reportService
): void {
$mediaTypes = $automatedReport->getMediaTypes();
// handle PDF or podcast
if (count($mediaTypes) === 1) {
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'status' => AutomatedReportResult::STATUS_DEFAULT,
'media_type' => $mediaTypes[0],
]
);
return;
}
// handle multiple media types
// create PDF as primary result
$this->reportResult = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PDF,
]
);
if (in_array(AutomatedReportsService::MEDIA_TYPE_PODCAST, $mediaTypes, true)) {
$this->reportResultPodcast = $reportService->createReportResult(
automatedReport: $automatedReport,
data: [
'media_type' => AutomatedReportsService::MEDIA_TYPE_PODCAST,
'parent_id' => $this->reportResult->getId(),
]
);
}
}
private function checkActivityCount(ProphetClient $prophetClient, array $payload, LoggerInterface $logger): bool
{
$logger->info(self::LOG_PREFIX . ' - Request activities count', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
'payload' => $payload,
]);
// validate expected activities count before sending request
$response = $prophetClient->sendRequest(
endpoint: ProphetClient::EXEC_REPORT_COUNT,
requestArray: $payload
);
$content = $response->getContent();
if (! isset($content['response'])) {
throw new ApiResponseException('Error getting activities count');
}
$logger->info(self::LOG_PREFIX . ' - Get activities count', $content);
$count = (int) $content['response'];
if ($count < self::MIN_ACTIVITIES_COUNT) {
$this->failReport(AutomatedReportResult::REASON_NOT_ENOUGH_ACTIVITIES);
$logger->info(self::LOG_PREFIX . ' - Not enough activities, skipped', [
'automatedReportUuid' => $this->reportUuid,
'reportUuid' => $this->reportResult->getUuid(),
]);
return false;
}
return true;
}
private function failReport(int $reason): void
{
if (isset($this->reportResult)) {
$this->reportResult->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
if (isset($this->reportResultPodcast)) {
$this->reportResultPodcast->update([
'status' => AutomatedReportResult::STATUS_FAILED,
'reason' => $reason,
]);
}
}
}
367
367
0
fa0cf643-d30e-43b8-ab3d-1fe18254609c
fa0cf643-d30e-43b8-ab3d-1fe18254609c
2026-04-17 07:14:23
459
459
0
<null>
<null>
1...
|
PhpStorm
|
faVsco.js – crm_configurations [EU]
|
NULL
|
42717
|