@@ -4,10 +4,16 @@ module Test.Meetings where
44
55import API.Galley
66import qualified API.GalleyInternal as I
7+ import Control.Monad.Reader (ask )
8+ import qualified Data.Text as Text
9+ import qualified Data.Text.Encoding as Text
710import Data.Time.Clock
811import qualified Data.Time.Format as Time
912import SetupHelpers
13+ import System.Timeout (timeout )
1014import Testlib.Prelude
15+ import Text.Regex.TDFA ((=~) )
16+ import UnliftIO.Concurrent (threadDelay )
1117
1218-- Helper to extract meetingId and domain from a meeting JSON object
1319getMeetingIdAndDomain :: (HasCallStack ) => Value -> App (String , String )
@@ -311,3 +317,93 @@ testMeetingRemoveInvitationNotFound = do
311317 let removeInvitation = object [" emails" .= [" alice@example.com" ]]
312318
313319 deleteMeetingInvitation owner " example.com" fakeMeetingId removeInvitation >>= assertStatus 404
320+
321+ testMeetingCleanup :: (HasCallStack ) => App ()
322+ testMeetingCleanup = do
323+ env <- ask
324+ timedOutResult <- liftIO $ timeout (2 * 60 * 1_000_000 ) $ runAppWithEnv env $ do
325+ -- 2 minutes timeout
326+ (owner, _tid, _members) <- createTeam OwnDomain 1
327+ now <- liftIO getCurrentTime
328+ -- Create a meeting that ends now.
329+ -- Configured retention is 0.0014 hours (~5 seconds).
330+ -- cutoffTime will be now' - 5s.
331+ -- We need end_date < cutoffTime.
332+ -- If we wait 6 seconds, now' = now + 6s.
333+ -- cutoffTime = now + 6s - 5s = now + 1s.
334+ -- end_date (now) < cutoffTime (now + 1s).
335+ let startTime = addUTCTime (negate 3600 ) now
336+ endTime = now
337+ newMeeting = defaultMeetingJson " Cleanup Test" startTime endTime []
338+
339+ r1 <- postMeetings owner newMeeting
340+ assertSuccess r1
341+ meeting <- getJSON 201 r1
342+ (meetingId, domain) <- getMeetingIdAndDomain meeting
343+
344+ -- Wait 6 seconds to ensure meeting is old enough
345+ liftIO $ threadDelay 6_000_000
346+
347+ -- Wait for cleanup job to run
348+ waitForCleanupJob OwnDomain
349+
350+ -- Check it's gone
351+ getMeeting owner domain meetingId >>= assertStatus 404
352+
353+ case timedOutResult of
354+ Just () -> pure ()
355+ Nothing -> assertFailure " testMeetingCleanup timed out after 2 minutes"
356+
357+ waitForCleanupJob :: (HasCallStack , MakesValue domain ) => domain -> App ()
358+ waitForCleanupJob domain = do
359+ initialMetrics <- getMetricsBody domain
360+ let initialCount = getRunCount initialMetrics
361+
362+ waitForIncrease domain initialCount
363+ where
364+ getMetricsBody d = do
365+ getMetrics d BackgroundWorker `bindResponse` \ resp -> do
366+ resp. status `shouldMatchInt` 200
367+ pure $ Text. unpack $ Text. decodeUtf8 resp. body
368+
369+ getRunCount metrics =
370+ let (_, _, _, matches) :: (String , String , String , [String ]) = (metrics =~ " wire_meetings_cleanup_runs_total ([0-9]+)" )
371+ in case matches of
372+ [val] -> read val :: Int
373+ _ -> 0
374+
375+ waitForIncrease d oldVal = do
376+ metrics <- getMetricsBody d
377+ let newVal = getRunCount metrics
378+ -- We wait until it increases.
379+ -- Note: if oldVal was 0 (metric didn't exist), getting 0 again means it hasn't run.
380+ -- If it runs, it should become >= 1.
381+ -- But wait, if matches is empty, we return 0.
382+ -- If the metric appears, it will be >= 1 (initialized at 0? Counter starts at 0).
383+ -- If it runs, it increments.
384+ when (newVal <= oldVal) $ do
385+ liftIO $ threadDelay 1_000_000 -- Wait 1s
386+ waitForIncrease d oldVal
387+
388+ testMeetingExpiration :: (HasCallStack ) => App ()
389+ testMeetingExpiration = do
390+ (owner, _tid, _members) <- createTeam OwnDomain 1
391+ now <- liftIO getCurrentTime
392+ let startTime = addUTCTime (negate 3600 ) now
393+ -- meetingValidityPeriodSeconds is configured to 5 seconds in galley.integration.yaml
394+ endTime = now
395+ newMeeting = defaultMeetingJson " Expiring Meeting" startTime endTime []
396+
397+ r1 <- postMeetings owner newMeeting
398+ assertSuccess r1
399+ meeting <- getJSON 201 r1
400+ (meetingId, domain) <- getMeetingIdAndDomain meeting
401+
402+ -- Check it is accessible immediately (endDate = now, so valid until now + 5s)
403+ getMeeting owner domain meetingId >>= assertStatus 200
404+
405+ -- Wait 6 seconds
406+ liftIO $ threadDelay 6_000_000
407+
408+ -- Check it is expired
409+ getMeeting owner domain meetingId >>= assertStatus 404
0 commit comments