module Puppet.Testing (testCatalog, Test, testFileSources) where import qualified Data.Map as Map import Puppet.Interpreter.Types import Data.List import Data.Maybe import Data.List.Utils import Data.Either import Control.Monad.Error import System.Posix.Files type TestResult = IO (Either String ()) data TestR = TestGroupR String [TestR] | SingleTestR String (Either String ()) deriving (Show) data Test = TestGroup String [Test] | TestFirstOk String [Test] | SingleTest String (FinalCatalog -> TestResult) failedTests :: TestR -> Maybe TestR failedTests (TestGroupR d tests) = case catMaybes (map failedTests tests) of [] -> Nothing x -> Just (TestGroupR d x) failedTests t@(SingleTestR _ (Left _)) = Just t failedTests _ = Nothing showRes :: TestR -> String showRes = showRes' 0 where showRes' :: Int -> TestR -> String showRes' dec (TestGroupR desc tsts) = replicate dec ' ' ++ desc ++ "\n" ++ unlines (map (showRes' (dec + 1)) tsts) showRes' dec (SingleTestR desc (Right ())) = replicate dec ' ' ++ desc ++ " OK" showRes' dec (SingleTestR desc (Left err)) = replicate dec ' ' ++ desc ++ " FAIL: " ++ err testFileSources :: String -> FinalCatalog -> Test testFileSources puppetdir cat = let fileresources = Map.elems $ Map.filterWithKey (\k _ -> fst k == "file") cat filesources = catMaybes $ map (Map.lookup "source" . rrparams) fileresources findPlaces :: String -> [String] findPlaces stringdir = let defaultsearch = puppetdir ++ "/files/" ++ stringdir in case split "/" stringdir of ("private":_) -> [puppetdir] -- not handled ("modules":modulename:rest) -> [puppetdir ++ "/modules/" ++ modulename ++ "/files/" ++ intercalate "/" rest, defaultsearch] _ -> [defaultsearch] checkSrcExists :: String -> FinalCatalog -> TestResult checkSrcExists src _ = runErrorT $ do let protostring = "puppet:///" unless (startswith protostring src) (throwError "Does not start with puppet:///") let stringdir = drop (length protostring) src places = findPlaces stringdir exists <- liftIO $ foldM (\c n -> if c then return True else fileExist n) False places unless exists (throwError $ "Searched in " ++ show places) return () genFileTest :: ResolvedValue -> Test genFileTest (ResolvedString src) = SingleTest (src ++ " exists") (checkSrcExists src) genFileTest (ResolvedArray arr) = TestFirstOk "First exists" (map genFileTest arr) genFileTest x = SingleTest ("Valid source") (\_ -> return $ Left ("Not a valid data type: " ++ show x)) in (TestGroup "check that all files are defined" (map genFileTest filesources)) unsingle :: TestR -> Either String () unsingle (SingleTestR desc (Left err)) = Left (desc ++ " failed: " ++ err) unsingle (SingleTestR _ _ ) = Right () unsingle x = Left ("Bad type for unsingle " ++ show x) runTest :: FinalCatalog -> Test -> IO TestR runTest cat (SingleTest desc test) = fmap (\x -> SingleTestR desc x) (test cat) runTest cat (TestGroup desc tests) = fmap (TestGroupR desc) (mapM (runTest cat) tests) runTest cat (TestFirstOk desc tests) = do allRes <- mapM (fmap unsingle . runTest cat) tests case lefts allRes of [] -> return $ SingleTestR desc (Right ()) x -> return $ SingleTestR desc (Left (show x)) runTests :: Test -> FinalCatalog -> IO (Either String ()) runTests tsts cat = do tr <- fmap failedTests (runTest cat tsts) case tr of Nothing -> return $ Right () Just fl -> return $ Left $ showRes fl testCatalog :: String -> FinalCatalog -> [Test] -> IO (Either String ()) testCatalog puppetdir catalog stests = runTests (TestGroup "All Tests" ( testFileSources puppetdir catalog : stests )) catalog