124 lines
5.2 KiB
Haskell
124 lines
5.2 KiB
Haskell
module Handler.Sheet.PersonalisedFilesSpec where
|
|
|
|
import TestImport
|
|
|
|
import Utils.Files
|
|
import Handler.Sheet.PersonalisedFiles
|
|
|
|
import qualified Yesod.Persist as Yesod
|
|
|
|
import ModelSpec ()
|
|
|
|
import Data.Universe.Class
|
|
|
|
import Data.Conduit
|
|
import qualified Data.Conduit.Combinators as C
|
|
import Control.Lens.Extras
|
|
import Control.Monad.Trans.Maybe
|
|
|
|
import System.FilePath (dropDrive)
|
|
|
|
import Data.Time.Clock (diffUTCTime)
|
|
import Data.Char (chr)
|
|
|
|
import Database.Persist.Sql (transactionUndo)
|
|
|
|
import Data.Bitraversable
|
|
|
|
import qualified Data.Set as Set
|
|
|
|
|
|
instance Arbitrary (FileReferenceResidual PersonalisedSheetFile) where
|
|
arbitrary = PersonalisedSheetFileResidual
|
|
<$> arbitrary
|
|
<*> arbitrary
|
|
<*> elements [ sfType | sfType <- universeF, sfType /= SheetMarking ]
|
|
|
|
instance Arbitrary PersonalisedSheetFilesDownloadAnonymous where
|
|
arbitrary = elements universeF
|
|
|
|
|
|
spec :: Spec
|
|
spec = withApp . describe "Personalised sheet file zip encoding" $ do
|
|
it "roundtrips" . replicateM_ 10 . runHandler . Yesod.runDB $ do
|
|
term <- liftIO $ generate arbitrary
|
|
tid <- insert term
|
|
school <- liftIO $ generate arbitrary
|
|
ssh <- insert school
|
|
course <- liftIO $ generate arbitrary <&> \c -> c { courseTerm = tid, courseSchool = ssh }
|
|
cid <- insert course
|
|
sheet <- liftIO $ generate arbitrary <&> \s -> s { sheetCourse = cid }
|
|
shid <- insert sheet
|
|
|
|
sheetFiles' <- liftIO . generate . listOf $ scale (`div` 2) arbitrary
|
|
sheetFiles <- fmap catMaybes . forM sheetFiles' $ \(f', res') -> runMaybeT $ do
|
|
let f = f' { fileTitle = filter (/= chr 0) $ fileTitle f' } -- PostgreSQL doesn't like to store NUL-bytes in text
|
|
guard . not . null . dropDrive $ fileTitle f
|
|
|
|
uid <-
|
|
let userLoop = do
|
|
user <- liftIO $ generate arbitrary
|
|
lift (insertUnique user) >>= maybe userLoop return
|
|
in userLoop
|
|
let res = res' { personalisedSheetFileResidualSheet = shid, personalisedSheetFileResidualUser = uid }
|
|
fRef <- lift (sinkFile (transFile generalize f) :: DB FileReference)
|
|
now <- liftIO getCurrentTime
|
|
void . lift . insert $ CourseParticipant cid (personalisedSheetFileResidualUser res) now Nothing CourseParticipantActive
|
|
void . lift . insert $ _FileReference # (fRef, res)
|
|
return (f, res)
|
|
|
|
anonMode <- liftIO $ generate arbitrary
|
|
|
|
let
|
|
fpL :: forall m. Lens' (Either PersonalisedSheetFile (File m)) FilePath
|
|
fpL = lens (either personalisedSheetFileTitle fileTitle) $ \f' path -> case f' of
|
|
Left pf -> Left pf { personalisedSheetFileTitle = path }
|
|
Right f -> Right f { fileTitle = path }
|
|
isDirectory = either (is _Nothing . personalisedSheetFileContent) (is _Nothing . fileContent)
|
|
|
|
loadFile :: Either (PersonalisedSheetFileUnresolved (Either PersonalisedSheetFile DBFile)) (Either PersonalisedSheetFile DBFile, FileReferenceResidual PersonalisedSheetFile)
|
|
-> DB (Either (PersonalisedSheetFileUnresolved (Either PersonalisedSheetFile PureFile)) (Either PersonalisedSheetFile PureFile, FileReferenceResidual PersonalisedSheetFile))
|
|
loadFile = bitraverse loadUnresolved loadResolved
|
|
where
|
|
loadUnresolved = traverse $ traverse toPureFile
|
|
loadResolved (f, fRes) = (, fRes) <$> traverse toPureFile f
|
|
|
|
recoveredFiles <- runConduit $
|
|
sourcePersonalisedSheetFiles cid (Just shid) Nothing anonMode Set.empty
|
|
.| resolvePersonalisedSheetFiles fpL isDirectory cid shid
|
|
.| C.mapM loadFile
|
|
.| C.foldMap pure
|
|
|
|
let
|
|
checkFile :: Either (PersonalisedSheetFileUnresolved (Either PersonalisedSheetFile PureFile)) (Either PersonalisedSheetFile PureFile, FileReferenceResidual PersonalisedSheetFile)
|
|
-> (PureFile, FileReferenceResidual PersonalisedSheetFile)
|
|
-> Bool
|
|
checkFile (Left _) _
|
|
= False
|
|
checkFile (Right (recFile, recResidual)) (file, residual)
|
|
= recResidual == residual
|
|
&& case recFile of
|
|
Right f -> f == file
|
|
Left pf -> dropDrive (fileTitle file) == dropDrive (personalisedSheetFileTitle pf)
|
|
&& abs (fileModified file `diffUTCTime` personalisedSheetFileModified pf) < 1e-6 -- Precision is a PostgreSQL limitation
|
|
&& fileReferenceContent (pureFileToFileReference file) == personalisedSheetFileContent pf
|
|
|
|
errors = go [] sheetFiles recoveredFiles
|
|
where go acc xs [] = reverse acc ++ map Left xs
|
|
go acc [] ys = reverse acc ++ map Right ys
|
|
go acc xs (y:ys)
|
|
| (xs', _ : xs'') <- break (checkFile y) xs
|
|
= go acc (xs' ++ xs'') ys
|
|
| is (_Left . _PSFUnresolved) y
|
|
, fromMaybe False $ previews (_Left . _PSFUnresolved . _Right . _fileTitle) ("meta-informationen" `isInfixOf`) y -- DEBUG; remove once _PSFUnresolvedCollatable works
|
|
= go acc xs ys
|
|
| isn't (_Left . _PSFUnresolved) y
|
|
, isn't _Right y
|
|
= go acc xs ys
|
|
| otherwise = go (Right y : acc) xs ys
|
|
|
|
unless (null errors) . liftIO $
|
|
expectationFailure $ show recoveredFiles ++ " does not match " ++ show sheetFiles ++ ": " ++ show errors
|
|
|
|
transactionUndo
|