1010namespace Unit \Listener ;
1111
1212use ChristophWurst \Nextcloud \Testing \TestCase ;
13+ use DateInterval ;
14+ use DateTime ;
1315use OCA \Mail \Account ;
1416use OCA \Mail \Db \MailAccount ;
17+ use OCA \Mail \Db \Mailbox ;
18+ use OCA \Mail \Db \Message ;
1519use OCA \Mail \Events \NewMessagesSynchronized ;
1620use OCA \Mail \Listener \NewMessagesSummarizeListener ;
1721use OCA \Mail \Service \AiIntegrations \AiIntegrationsService ;
1822use OCP \AppFramework \Services \IAppConfig ;
23+ use OCP \AppFramework \Utility \ITimeFactory ;
1924use PHPUnit \Framework \MockObject \MockObject ;
2025use Psr \Log \LoggerInterface ;
2126use Psr \Log \Test \TestLogger ;
2227
2328class NewMessagesSummarizeListenerTest extends TestCase {
29+ private const NOW = 1_750_000_000 ;
30+
2431 private LoggerInterface $ logger ;
2532 private NewMessagesSummarizeListener $ listener ;
26- private AiIntegrationsService |MockObject $ aiService ;
27- private IAppConfig |MockObject $ appConfig ;
33+ private AiIntegrationsService &MockObject $ aiService ;
34+ private IAppConfig &MockObject $ appConfig ;
35+ private ITimeFactory &MockObject $ timeFactory ;
2836
2937 protected function setUp (): void {
3038 parent ::setUp ();
3139
3240 $ this ->logger = new TestLogger ();
3341 $ this ->aiService = $ this ->createMock (AiIntegrationsService::class);
3442 $ this ->appConfig = $ this ->createMock (IAppConfig::class);
43+ $ this ->timeFactory = $ this ->createMock (ITimeFactory::class);
44+ $ this ->timeFactory ->method ('getDateTime ' )
45+ ->willReturnCallback (static fn () => new DateTime ('@ ' . self ::NOW ));
3546
3647 $ this ->listener = new NewMessagesSummarizeListener (
3748 $ this ->logger ,
3849 $ this ->aiService ,
39- $ this ->appConfig
50+ $ this ->appConfig ,
51+ $ this ->timeFactory ,
4052 );
4153 }
4254
43- public function testLlmEnabled (): void {
44- $ event = $ this ->createMock (NewMessagesSynchronized::class);
45- $ account = new Account (new MailAccount ());
46- $ event ->expects ($ this ->once ())
47- ->method ('getAccount ' )
48- ->willReturn ($ account );
49- $ event ->expects ($ this ->once ())
50- ->method ('getMessages ' )
51- ->willReturn ([]);
55+ private function createMailbox (int $ id , string $ specialUse = '[] ' ): Mailbox {
56+ $ mailbox = new Mailbox ();
57+ $ mailbox ->setId ($ id );
58+ $ mailbox ->setSpecialUse ($ specialUse );
59+ return $ mailbox ;
60+ }
61+
62+ private function createMessage (int $ sentAt ): Message {
63+ $ message = new Message ();
64+ $ message ->setSentAt ($ sentAt );
65+ return $ message ;
66+ }
67+
68+ private function enableLlm (): void {
5269 $ this ->appConfig ->expects ($ this ->once ())
5370 ->method ('getAppValueBool ' )
5471 ->with ('llm_processing ' , false )
5572 ->willReturn (true );
73+ }
74+
75+ private function maxAgeCutoff (): int {
76+ return (new DateTime ('@ ' . self ::NOW ))
77+ ->sub (new DateInterval (AiIntegrationsService::RECENT_MESSAGE_MAX_AGE ))
78+ ->getTimestamp ();
79+ }
80+
81+ public function testLlmEnabled (): void {
82+ $ account = new Account (new MailAccount ());
83+ $ message = $ this ->createMessage (self ::NOW );
84+ $ event = new NewMessagesSynchronized ($ account , $ this ->createMailbox (1 ), [$ message ]);
85+ $ this ->enableLlm ();
5686 $ this ->aiService ->expects ($ this ->once ())
5787 ->method ('summarizeMessages ' )
58- ->with ($ account , []);
88+ ->with ($ account , [$ message ]);
89+
5990 $ this ->listener ->handle ($ event );
6091 }
6192
@@ -67,7 +98,82 @@ public function testLlmDisabled(): void {
6798 ->willReturn (false );
6899 $ this ->aiService ->expects ($ this ->never ())
69100 ->method ('summarizeMessages ' );
101+
70102 $ this ->listener ->handle ($ event );
71103 }
72104
105+ public static function provideSkippedSpecialUses (): array {
106+ return [
107+ 'all ' => ['["all"] ' ],
108+ 'archive ' => ['["archive"] ' ],
109+ 'drafts ' => ['["drafts"] ' ],
110+ 'flagged ' => ['["flagged"] ' ],
111+ 'junk ' => ['["junk"] ' ],
112+ 'sent ' => ['["sent"] ' ],
113+ 'trash ' => ['["trash"] ' ],
114+ ];
115+ }
116+
117+ /**
118+ * @dataProvider provideSkippedSpecialUses
119+ */
120+ public function testSkipsSpecialUseMailbox (string $ specialUse ): void {
121+ $ account = new Account (new MailAccount ());
122+ $ event = new NewMessagesSynchronized ($ account , $ this ->createMailbox (1 , $ specialUse ), [$ this ->createMessage (self ::NOW )]);
123+ $ this ->enableLlm ();
124+ $ this ->aiService ->expects ($ this ->never ())
125+ ->method ('summarizeMessages ' );
126+
127+ $ this ->listener ->handle ($ event );
128+ }
129+
130+ public static function provideAccountMailboxSetters (): array {
131+ return [
132+ 'archive ' => ['setArchiveMailboxId ' ],
133+ 'drafts ' => ['setDraftsMailboxId ' ],
134+ 'junk ' => ['setJunkMailboxId ' ],
135+ 'sent ' => ['setSentMailboxId ' ],
136+ 'snooze ' => ['setSnoozeMailboxId ' ],
137+ 'trash ' => ['setTrashMailboxId ' ],
138+ ];
139+ }
140+
141+ /**
142+ * @dataProvider provideAccountMailboxSetters
143+ */
144+ public function testSkipsAccountConfiguredMailbox (string $ setter ): void {
145+ $ mailAccount = new MailAccount ();
146+ $ mailAccount ->$ setter (7 );
147+ $ account = new Account ($ mailAccount );
148+ $ event = new NewMessagesSynchronized ($ account , $ this ->createMailbox (7 ), [$ this ->createMessage (self ::NOW )]);
149+ $ this ->enableLlm ();
150+ $ this ->aiService ->expects ($ this ->never ())
151+ ->method ('summarizeMessages ' );
152+
153+ $ this ->listener ->handle ($ event );
154+ }
155+
156+ public function testOnlySummarizesRecentMessages (): void {
157+ $ account = new Account (new MailAccount ());
158+ $ recent = $ this ->createMessage (self ::NOW );
159+ $ atCutoff = $ this ->createMessage ($ this ->maxAgeCutoff ());
160+ $ old = $ this ->createMessage ($ this ->maxAgeCutoff () - 1 );
161+ $ event = new NewMessagesSynchronized ($ account , $ this ->createMailbox (1 ), [$ old , $ recent , $ atCutoff ]);
162+ $ this ->enableLlm ();
163+ $ this ->aiService ->expects ($ this ->once ())
164+ ->method ('summarizeMessages ' )
165+ ->with ($ account , [$ recent , $ atCutoff ]);
166+
167+ $ this ->listener ->handle ($ event );
168+ }
169+
170+ public function testSkipsWhenAllMessagesAreOld (): void {
171+ $ account = new Account (new MailAccount ());
172+ $ event = new NewMessagesSynchronized ($ account , $ this ->createMailbox (1 ), [$ this ->createMessage ($ this ->maxAgeCutoff () - 1 )]);
173+ $ this ->enableLlm ();
174+ $ this ->aiService ->expects ($ this ->never ())
175+ ->method ('summarizeMessages ' );
176+
177+ $ this ->listener ->handle ($ event );
178+ }
73179}
0 commit comments