packages feed

cutesetup-0.1.0.0: test/Spec.hs

module Main (main) where

import Test.QuickCheck
import System.Exit (exitFailure, exitSuccess)
import Control.Monad (unless)
import Data.List (isInfixOf, isPrefixOf)
import qualified Data.Map.Strict as Map

import Languages
import NixGen
import NixSearch (Channel(..))

-- Generate arbitrary Language
instance Arbitrary Language where
  arbitrary = arbitraryBoundedEnum

-- Generate arbitrary Channel
instance Arbitrary Channel where
  arbitrary = arbitraryBoundedEnum

-- Generate arbitrary LangPackage
instance Arbitrary LangPackage where
  arbitrary = do
    name <- listOf1 (elements ['a'..'z'])
    label <- listOf1 (elements ['a'..'z'])
    desc <- listOf (elements (['a'..'z'] ++ [' ']))
    def <- arbitrary
    return (LangPackage name label desc def)

-- Generate arbitrary GenerateConfig
instance Arbitrary GenerateConfig where
  arbitrary = do
    lang <- arbitrary
    pkgs <- listOf arbitrary
    extraAttrs <- listOf $ do
      attr <- listOf1 (elements ['a'..'z'])
      chan <- arbitrary
      return (attr, chan)
    targetDir <- listOf (elements (['a'..'z'] ++ ['/']))
    systemArch <- elements ["x86_64-linux", "aarch64-linux"]
    pythonVenv <- arbitrary
    nixpkgsStable <- elements ["nixos-25.11", "nixos-24.11"]
    return $ GenerateConfig
      { gcLang = lang
      , gcPackages = pkgs
      , gcExtraAttrs = extraAttrs
      , gcTargetDir = targetDir
      , gcSystemArch = systemArch
      , gcPythonVenv = pythonVenv
      , gcNixpkgsStable = nixpkgsStable
      }

-- Property: Generated flake always starts with a valid Nix brace layout
prop_validFlakeBraceLayout :: GenerateConfig -> Bool
prop_validFlakeBraceLayout cfg =
  let flake = generateFlake cfg
  in "{" `isPrefixOf` flake && "}" `isInfixOf` flake

-- Property: Package inclusion in flake nativeBuildInputs
prop_allPackagesIncluded :: GenerateConfig -> Bool
prop_allPackagesIncluded cfg =
  let flake = generateFlake cfg
      -- All unstable packages in gcPackages should be in nativeBuildInputs as pkgs.<name>
      unstablePkgs = [pkgName p | p <- gcPackages cfg] ++ [attr | (attr, Unstable) <- gcExtraAttrs cfg]
      -- All stable packages in gcExtraAttrs should be as pkgs-stable.<name>
      stablePkgs = [attr | (attr, Stable) <- gcExtraAttrs cfg]
      
      checkUnstable = all (\p -> ("pkgs." ++ p) `isInfixOf` flake) unstablePkgs
      checkStable = all (\p -> ("pkgs-stable." ++ p) `isInfixOf` flake) stablePkgs
  in checkUnstable && checkStable

-- Property: Python virtual environment shellHook logic
prop_pythonVenvHook :: GenerateConfig -> Bool
prop_pythonVenvHook cfg =
  let flake = generateFlake cfg
      hasVenv = gcPythonVenv cfg
  in if hasVenv
     then "python3 -m venv .venv" `isInfixOf` flake && "source .venv/bin/activate" `isInfixOf` flake
     else not ("source .venv/bin/activate" `isInfixOf` flake)

-- Property: Correct language description in the header/description field
prop_correctDescription :: GenerateConfig -> Bool
prop_correctDescription cfg =
  let flake = generateFlake cfg
      expectedDesc = show (gcLang cfg) ++ " development environment"
  in expectedDesc `isInfixOf` flake

-- Property: .envrc is always correctly generated
prop_validEnvrc :: GenerateConfig -> Bool
prop_validEnvrc cfg =
  let envrc = generateEnvrc cfg
  in "use flake" `isInfixOf` envrc && "# Generated by cutesetup" `isInfixOf` envrc

runProperty :: Testable prop => String -> prop -> IO Bool
runProperty name prop = do
  putStrLn $ "Running property: " ++ name
  res <- quickCheckResult prop
  case res of
    Success {} -> return True
    _ -> return False

main :: IO ()
main = do
  putStrLn "=== Running Cutesetup Property Tests ==="
  r1 <- runProperty "valid flake brace layout" prop_validFlakeBraceLayout
  r2 <- runProperty "all packages included in inputs" prop_allPackagesIncluded
  r3 <- runProperty "python venv shellHook activation" prop_pythonVenvHook
  r4 <- runProperty "correct description field" prop_correctDescription
  r5 <- runProperty "valid envrc structure" prop_validEnvrc
  
  let success = r1 && r2 && r3 && r4 && r5
  if success
    then do
      putStrLn "All property tests passed!"
      exitSuccess
    else do
      putStrLn "Some tests failed."
      exitFailure