test(avs): add more encodings and test cases vor avs datatypes

This commit is contained in:
Steffen Jost 2022-11-23 19:27:41 +01:00
parent afa1ceff20
commit d125bcdc9c
4 changed files with 74 additions and 11 deletions

View File

@ -2,6 +2,8 @@
-- --
-- SPDX-License-Identifier: AGPL-3.0-or-later -- SPDX-License-Identifier: AGPL-3.0-or-later
{-# LANGUAGE TypeApplications #-}
module Handler.Utils.Avs module Handler.Utils.Avs
( upsertAvsUser, upsertAvsUserById, upsertAvsUserByCard ( upsertAvsUser, upsertAvsUserById, upsertAvsUserByCard
, getLicence, getLicenceDB , getLicence, getLicenceDB
@ -27,6 +29,10 @@ import Foundation.Yesod.Auth (upsertCampusUserByCn,CampusUserConversionException
import Handler.Utils.Company import Handler.Utils.Company
import Handler.Users.Add import Handler.Users.Add
--import qualified Database.Esqueleto.Experimental as E -- needs TypeApplications Lang-Pragma
--import qualified Database.Esqueleto.Utils as E
-------------------- --------------------
-- AVS Exceptions -- -- AVS Exceptions --
-------------------- --------------------
@ -84,7 +90,7 @@ setLicence uid lic = do
setLicenceAvs :: AvsPersonId -> AvsLicence -> DB () setLicenceAvs :: AvsPersonId -> AvsLicence -> DB ()
setLicenceAvs apid lic = do setLicenceAvs apid lic = do
let req = Set.singleton $ AvsPersonLicence apid lic let req = Set.singleton $ AvsPersonLicence { avsLicenceRampLicence = lic, avsLicencePersonID = apid }
setLicencesAvs req setLicencesAvs req
-- setLicencesAvs :: Set AvsPersonLicence -> DB () -- setLicencesAvs :: Set AvsPersonLicence -> DB ()
@ -95,7 +101,8 @@ setLicencesAvs pls = do
response <- throwLeftM . avsQuerySetLicences $ AvsQuerySetLicences pls response <- throwLeftM . avsQuerySetLicences $ AvsQuerySetLicences pls
case response of case response of
AvsResponseSetLicencesError{..} -> do AvsResponseSetLicencesError{..} -> do
$logErrorS "AVS" $ "Set licence failed completely: " <> avsResponseSetLicencesStatus <> ". Details: " <> avsResponseSetLicencesMessage let msg = "Set licence failed completely: " <> avsResponseSetLicencesStatus <> ". Details: " <> avsResponseSetLicencesMessage
$logErrorS "AVS" msg
throwM $ AvsSetLicencesFailed avsResponseSetLicencesStatus throwM $ AvsSetLicencesFailed avsResponseSetLicencesStatus
AvsResponseSetLicences responses -> AvsResponseSetLicences responses ->
forM_ responses $ \AvsLicenceResponse{..} -> forM_ responses $ \AvsLicenceResponse{..} ->
@ -109,8 +116,23 @@ setLicencesAvs pls = do
-- TODO: turn into a job, once the interface is actually available -- TODO: turn into a job, once the interface is actually available
checkLicences :: Handler () checkLicences :: Handler ()
checkLicences = do checkLicences = do
AvsQuery{..} <- maybeThrowM AvsInterfaceUnavailable $ getsYesod $ view _appAvsQuery {-
AvsResponseGetLicences _licences <- throwLeftM avsQueryGetAllLicences AvsQuery{..} <- maybeThrowM AvsInterfaceUnavailable $ getsYesod $ view _appAvsQuery
AvsResponseGetLicences licences <- throwLeftM avsQueryGetAllLicences
--let (vorfeld, nonvorfeld) = Set.partition (`avsPersonLicenceIs` AvsLicenceVorfeld) licences
-- rollfeld = Set.filter (`avsPersonLicenceIs` AvsLicenceRollfeld) nonvorfeld
let (noOrVorfeld, rollfeld) = Set.spanAntitone (`avsPersonLicenceIsLEQ` AvsLicenceVorfeld) licences
(_nolicence , vorfeld) = Set.spanAntitone (`avsPersonLicenceIsLEQ` AvsNoLicence) noOrVorfeld
now <- liftIO getCurrentTime
runDB $ do
E.select $ do
-}
--TODO this must be chunked into separate jobs/tasks --TODO this must be chunked into separate jobs/tasks
--forM licences $ \AvsPersonLicence{..} -> do --forM licences $ \AvsPersonLicence{..} -> do
error "CONTINUE HERE" -- TODO STUB error "CONTINUE HERE" -- TODO STUB

View File

@ -164,8 +164,9 @@ discernAvsIds someid = aux someid
| otherwise = Nothing | otherwise = Nothing
aux _ = Right . AvsPersonId <$> readMay someid -- must always succeed at that point aux _ = Right . AvsPersonId <$> readMay someid -- must always succeed at that point
data AvsLicence = AvsNoLicence | AvsLicenceVorfeld | AvsLicenceRollfeld data AvsLicence = AvsNoLicence | AvsLicenceVorfeld | AvsLicenceRollfeld
deriving (Bounded, Enum, Eq, Ord, Read, Show, Generic, Typeable) deriving (Bounded, Enum, Eq, Ord, Read, Show, Generic, Typeable, Finite, Universe)
instance ToJSON AvsLicence where instance ToJSON AvsLicence where
-- toJSON al = Number $ fromEnum AvsLicence -- would do, but... -- toJSON al = Number $ fromEnum AvsLicence -- would do, but...
@ -176,13 +177,26 @@ instance ToJSON AvsLicence where
instance FromJSON AvsLicence where instance FromJSON AvsLicence where
parseJSON (Number n) | n == 1 = pure AvsLicenceVorfeld -- ordered by occurrence, n==1 is most common case parseJSON (Number n) | n == 1 = pure AvsLicenceVorfeld -- ordered by occurrence, n==1 is most common case
| n == 2 = pure AvsLicenceRollfeld | n == 2 = pure AvsLicenceRollfeld
| n == 0 = pure AvsNoLicence -- n==0 never received from AVS, only sent to AVS | n == 0 = pure AvsNoLicence -- n==0 never received from AVS, only sent to AVS
#ifdef DEVELOPMENT #ifdef DEVELOPMENT
parseJSON invalid = prependFailure "parsing AvsLicence failed, " $ fail $ "expected Int value being 0, 1 or 2. Found " ++ show invalid parseJSON invalid = prependFailure "parsing AvsLicence failed, " $ fail $ "expected Int value being 0, 1 or 2. Found " ++ show invalid
#else #else
parseJSON _ = pure AvsNoLicence -- we simply ignore all other values parseJSON _ = pure AvsNoLicence -- we simply ignore all other values
#endif #endif
-- we assume that the Ord-Instance is respected by the SQL Backend!
instance PersistField AvsLicence where
toPersistValue = PersistInt64 . fromIntegral . fromEnum
fromPersistValue (PersistInt64 v')
| let v = fromIntegral v'
, v >= fromEnum (minBound::AvsLicence)
, v <= fromEnum (maxBound::AvsLicence)
= Right $ toEnum v
fromPersistValue other = Left $ "Encoding of AvsLicence " <> tshow other <> " is out of range"
instance PersistFieldSql AvsLicence where
sqlType _ = SqlInt64
-- | Ought to be identical to QualificationShortname! -- | Ought to be identical to QualificationShortname!
licence2char :: AvsLicence -> Char licence2char :: AvsLicence -> Char
licence2char AvsNoLicence = '0' licence2char AvsNoLicence = '0'
@ -352,8 +366,8 @@ deriveJSON defaultOptions
-} -}
data AvsPersonLicence = AvsPersonLicence data AvsPersonLicence = AvsPersonLicence
{ avsLicencePersonID :: AvsPersonId { avsLicenceRampLicence :: AvsLicence
, avsLicenceRampLicence :: AvsLicence , avsLicencePersonID :: AvsPersonId
} }
deriving (Eq, Ord, Show, Generic, Typeable) deriving (Eq, Ord, Show, Generic, Typeable)
deriveJSON defaultOptions deriveJSON defaultOptions
@ -363,6 +377,13 @@ deriveJSON defaultOptions
, rejectUnknownFields = False , rejectUnknownFields = False
} ''AvsPersonLicence } ''AvsPersonLicence
avsPersonLicenceIs :: AvsPersonLicence -> AvsLicence -> Bool
avsPersonLicenceIs = (==) . avsLicenceRampLicence
avsPersonLicenceIsLEQ :: AvsPersonLicence -> AvsLicence -> Bool
avsPersonLicenceIsLEQ = (<=) . avsLicenceRampLicence
data AvsLicenceResponse = AvsLicenceResponse data AvsLicenceResponse = AvsLicenceResponse
{ avsResponsePersonID :: AvsPersonId { avsResponsePersonID :: AvsPersonId
, avsResponseSuccess :: SloppyBool , avsResponseSuccess :: SloppyBool

View File

@ -623,10 +623,7 @@ mTuple = liftA2 (,)
-- Lists -- -- Lists --
----------- -----------
<<<<<<< HEAD
=======
-- avoids some parenthesis within guards -- avoids some parenthesis within guards
>>>>>>> master
notNull :: MonoFoldable mono => mono -> Bool notNull :: MonoFoldable mono => mono -> Bool
notNull = not . null notNull = not . null

View File

@ -7,6 +7,9 @@ module Utils.TypesSpec where
import TestImport import TestImport
import Utils import Utils
import qualified Data.Aeson as Aeson
instance Arbitrary SloppyBool where instance Arbitrary SloppyBool where
arbitrary = SloppyBool <$> arbitrary arbitrary = SloppyBool <$> arbitrary
shrink (SloppyBool x) = SloppyBool <$> shrink x shrink (SloppyBool x) = SloppyBool <$> shrink x
@ -95,6 +98,8 @@ spec = do
[ eqLaws, ordLaws, showLaws, jsonLaws ] [ eqLaws, ordLaws, showLaws, jsonLaws ]
lawsCheckHspec (Proxy @AvsDataPerson) --iso failed lawsCheckHspec (Proxy @AvsDataPerson) --iso failed
[ eqLaws, ordLaws, showLaws, jsonLaws ] [ eqLaws, ordLaws, showLaws, jsonLaws ]
lawsCheckHspec (Proxy @AvsLicence)
[ eqLaws, ordLaws, showLaws, jsonLaws, persistFieldLaws ]
lawsCheckHspec (Proxy @AvsPersonLicence) lawsCheckHspec (Proxy @AvsPersonLicence)
[ eqLaws, ordLaws, showLaws, jsonLaws ] [ eqLaws, ordLaws, showLaws, jsonLaws ]
lawsCheckHspec (Proxy @AvsLicenceResponse) lawsCheckHspec (Proxy @AvsLicenceResponse)
@ -116,6 +121,24 @@ spec = do
lawsCheckHspec (Proxy @AvsQuerySetLicences) lawsCheckHspec (Proxy @AvsQuerySetLicences)
[ eqLaws, showLaws, jsonLaws] [ eqLaws, showLaws, jsonLaws]
describe "AvsLicence" $ do
it "ordering is consistent with its PersistField instance" . property $ -- this assumption is used in Handler.Utils.Avs.checkLicences
\a (b :: AvsLicence) -> compare a b == compare (toPersistValue a) (toPersistValue b)
it "assigns AvsLicence fixed SQL values" . example $ do -- ensure that DB encoding does not change unnoticed
toPersistValue AvsLicenceVorfeld `shouldBe` toPersistValue (1::Int64)
toPersistValue AvsLicenceRollfeld `shouldBe` toPersistValue (2::Int64)
it "assigns AvsLicence fixed JSON values" . example $ do -- ensure that SQL encoding does not change unnoticed
Aeson.toJSON AvsLicenceVorfeld `shouldBe` Aeson.Number 1
Aeson.toJSON AvsLicenceRollfeld `shouldBe` Aeson.Number 2
describe "Ord AvsPersonLicence" $ do
it "proritises avsLicenceRampLicence" . property $
\p0 p1@AvsPersonLicence{avsLicenceRampLicence=v1} ->
let p2@AvsPersonLicence{avsLicenceRampLicence=v2} = p0 in
(v1 /= v2) ==> compare p1 p2 == compare v1 v2
it "has antitone Function avsPersonLicenceIsGEQ" . property $ -- this assumption is used in Handler.Utils.Avs.checkLicences
\j k l -> j < k ==> avsPersonLicenceIsLEQ j l >= avsPersonLicenceIsLEQ k l
describe "Ord AvsDataCard" $ do describe "Ord AvsDataCard" $ do
it "prioritises avsDataValid" . property $ it "prioritises avsDataValid" . property $
\p0 p1@AvsDataPersonCard{avsDataValid=v1} -> \p0 p1@AvsDataPersonCard{avsDataValid=v1} ->