@@ -5,14 +5,16 @@ import { processPresence } from './presenceEngine';
55
66const ONE_HOUR = 3600_000 ;
77
8- type PresenceUser = Pick < IUser , 'statusDefault' | 'statusSource' | 'statusText' | 'statusExpiresAt' | 'previousState' > ;
8+ type PresenceUser = Pick < IUser , 'type' | 'roles' | ' statusDefault' | 'statusSource' | 'statusText' | 'statusExpiresAt' | 'previousState' > ;
99
1010const user = ( data : Partial < PresenceUser > = { } ) : PresenceUser => ( {
1111 statusDefault : UserStatus . ONLINE ,
1212 statusText : '' ,
1313 ...data ,
1414} ) ;
1515
16+ const bot = ( data : Partial < PresenceUser > = { } ) : PresenceUser => user ( { type : 'bot' , ...data } ) ;
17+
1618const session = ( status : UserStatus = UserStatus . ONLINE ) : IUserSessionConnection => ( {
1719 id : 'random' ,
1820 instanceId : 'random' ,
@@ -61,6 +63,40 @@ describe('processPresence', () => {
6163 } ) ;
6264 } ) ;
6365
66+ describe ( 'auto-away over an existing claim (claimless recompute)' , ( ) => {
67+ test ( 'a manual online claim still goes AWAY on idle, without touching the claim or statusText' , ( ) => {
68+ const result = processPresence ( user ( { statusSource : 'manual' , statusDefault : UserStatus . ONLINE , statusText : 'Working' } ) , [
69+ session ( UserStatus . AWAY ) ,
70+ ] ) ;
71+
72+ expect ( result . values ) . toMatchObject ( { status : UserStatus . AWAY , statusConnection : UserStatus . AWAY } ) ;
73+ expect ( result . values ) . not . toHaveProperty ( 'statusText' ) ;
74+ expect ( result . values ) . not . toHaveProperty ( 'statusSource' ) ;
75+ expect ( result . values ) . not . toHaveProperty ( 'statusDefault' ) ;
76+ expect ( result . clear ) . toBeUndefined ( ) ;
77+ } ) ;
78+
79+ test ( 'a manual busy claim stays BUSY on idle (auto-away suppressed), claim preserved' , ( ) => {
80+ const result = processPresence ( user ( { statusSource : 'manual' , statusDefault : UserStatus . BUSY , statusText : 'Focusing' } ) , [
81+ session ( UserStatus . AWAY ) ,
82+ ] ) ;
83+
84+ expect ( result . values ) . toMatchObject ( { status : UserStatus . BUSY , statusConnection : UserStatus . AWAY } ) ;
85+ expect ( result . values ) . not . toHaveProperty ( 'statusText' ) ;
86+ expect ( result . values ) . not . toHaveProperty ( 'statusSource' ) ;
87+ expect ( result . clear ) . toBeUndefined ( ) ;
88+ } ) ;
89+
90+ test ( 'when the user becomes active again the manual online claim resolves back to ONLINE' , ( ) => {
91+ const result = processPresence ( user ( { statusSource : 'manual' , statusDefault : UserStatus . ONLINE , statusText : 'Working' } ) , [
92+ session ( UserStatus . ONLINE ) ,
93+ ] ) ;
94+
95+ expect ( result . values ) . toMatchObject ( { status : UserStatus . ONLINE , statusConnection : UserStatus . ONLINE } ) ;
96+ expect ( result . values ) . not . toHaveProperty ( 'statusText' ) ;
97+ } ) ;
98+ } ) ;
99+
64100 describe ( 'setActive' , ( ) => {
65101 test ( 'should apply manual claim when user is online' , ( ) => {
66102 const result = processPresence ( user ( ) , [ session ( ) ] , {
@@ -103,14 +139,15 @@ describe('processPresence', () => {
103139 expect ( result . values . previousState ) . toBeUndefined ( ) ;
104140 } ) ;
105141
106- test ( 'should overwrite when same priority claim arrives ' , ( ) => {
142+ test ( 'should display the new claim and save the displaced one as previousState on same priority ' , ( ) => {
107143 const result = processPresence (
108144 user ( { statusSource : 'internal' , statusDefault : UserStatus . BUSY , statusText : 'On a call' } ) ,
109145 [ session ( ) ] ,
110146 { type : 'setActive' , newState : { statusDefault : UserStatus . BUSY , statusText : 'In a meeting' , statusSource : 'internal' } } ,
111147 ) ;
112148 expect ( result . values . statusText ) . toBe ( 'In a meeting' ) ;
113149 expect ( result . values . statusSource ) . toBe ( 'internal' ) ;
150+ expect ( result . values . previousState ) . toMatchObject ( { statusSource : 'internal' , statusText : 'On a call' } ) ;
114151 } ) ;
115152
116153 test ( 'should queue lower priority claim as previousState' , ( ) => {
@@ -331,4 +368,44 @@ describe('processPresence', () => {
331368 expect ( result . values . status ) . toBe ( UserStatus . OFFLINE ) ;
332369 } ) ;
333370 } ) ;
371+
372+ describe ( 'no connection - humans (connection-bound)' , ( ) => {
373+ test ( 'clearActive: explicit "set online" is honored (stays online)' , ( ) => {
374+ const result = processPresence ( user ( { statusSource : 'manual' , statusDefault : UserStatus . BUSY } ) , [ ] , { type : 'clearActive' } ) ;
375+ expect ( result . values . status ) . toBe ( UserStatus . ONLINE ) ;
376+ } ) ;
377+
378+ test ( 'setActive: busy claim is persisted but display is OFFLINE' , ( ) => {
379+ const result = processPresence ( user ( ) , [ ] , {
380+ type : 'setActive' ,
381+ newState : { statusDefault : UserStatus . BUSY , statusSource : 'manual' , statusText : 'Focus' } ,
382+ } ) ;
383+ expect ( result . values . status ) . toBe ( UserStatus . OFFLINE ) ;
384+ expect ( result . values . statusDefault ) . toBe ( UserStatus . BUSY ) ; // claim persisted for reconnect
385+ } ) ;
386+
387+ test ( 'endActive: ending a claim reverts to connection reality (OFFLINE), not statusDefault' , ( ) => {
388+ const result = processPresence ( user ( { statusSource : 'manual' , statusDefault : UserStatus . BUSY } ) , [ ] , { type : 'endActive' } ) ;
389+ expect ( result . values . status ) . toBe ( UserStatus . OFFLINE ) ;
390+ } ) ;
391+ } ) ;
392+
393+ describe ( 'no connection - service users (bots/apps) hold their declared status' , ( ) => {
394+ test . each ( [
395+ [ 'type "bot"' , bot ( ) , UserStatus . BUSY , 'manual' as const ] ,
396+ [ 'type "app"' , user ( { type : 'app' } ) , UserStatus . AWAY , 'external' as const ] ,
397+ [ 'role "bot" (type "user")' , user ( { type : 'user' , roles : [ 'bot' ] } ) , UserStatus . BUSY , 'manual' as const ] ,
398+ ] ) ( 'setActive: a service user (%s) displays its declared status, not OFFLINE' , ( _label , serviceUser , statusDefault , statusSource ) => {
399+ const result = processPresence ( serviceUser , [ ] , {
400+ type : 'setActive' ,
401+ newState : { statusDefault, statusSource } ,
402+ } ) ;
403+ expect ( result . values . status ) . toBe ( statusDefault ) ;
404+ } ) ;
405+
406+ test ( 'endActive: a bot whose claim is released reverts to OFFLINE (re-assert to restore)' , ( ) => {
407+ const result = processPresence ( bot ( { statusSource : 'manual' , statusDefault : UserStatus . BUSY } ) , [ ] , { type : 'endActive' } ) ;
408+ expect ( result . values . status ) . toBe ( UserStatus . OFFLINE ) ;
409+ } ) ;
410+ } ) ;
334411} ) ;
0 commit comments