packages feed

canadian-income-tax-2023.0: test/Regression.hs

{-# LANGUAGE Haskell2010 #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE NoFieldSelectors #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Main where

import Tax.Canada.Province.ON qualified as ON
import Tax.Canada.Province.AB qualified as AB (t1Fields)
import Tax.Canada.Province.BC qualified as BC (t1Fields)
import Tax.Canada.T1 (T1, fixT1)
import Tax.FDF as FDF

import Control.Monad.IO.Class (liftIO)
import Data.ByteString qualified as ByteString
import Data.ByteString.Lazy (fromStrict)
import Data.Foldable (toList)
import Data.Functor.Const (Const (Const, getConst))
import Data.List qualified as List
import Data.Map qualified as Map
import Data.Maybe (fromMaybe)
import Data.Semigroup (All (All, getAll))
import Data.Text (Text)
import Data.Text qualified as Text
import Rank2 qualified
import System.Directory (doesDirectoryExist, listDirectory)
import System.Exit (die)
import System.FilePath.Posix (combine)
import Text.FDF (FDF, parse, serialize)
import Text.FDF qualified

import Test.Tasty
import Test.Tasty.Golden

main = listDirectory inputDir >>= traverse test >>= defaultMain . testGroup "Regression"

rootDir, inputDir, outputDir, referenceDir :: FilePath
rootDir = combine "test" "regression"
inputDir = combine rootDir "inputs"
outputDir = combine rootDir "outputs"
referenceDir = combine rootDir "reference"

test :: FilePath -> IO TestTree
test path = do
  let inputPath = combine inputDir path
  isDir <- liftIO (doesDirectoryExist inputPath)
  if isDir
    then testReturn path
    else testT1 path

testT1 :: FilePath -> IO TestTree
testT1 path = do
  fdfBytes <- ByteString.readFile $ combine inputDir path
  case parse fdfBytes of
    Left err -> die err
    Right fdf ->
      pure $
        goldenVsString path
          (combine referenceDir path)
          (pure $ fromStrict $ serialize $ either error id $ FDF.mapForm ON.t1Fields fixT1 fdf)

testReturn :: FilePath -> IO TestTree
testReturn path = do
  let inputPath = combine inputDir path
  fdfFileNames <- listDirectory inputPath
  fdfBytes <- traverse (ByteString.readFile . combine inputPath) fdfFileNames
  case traverse parse fdfBytes of
    Left err -> die err
    Right fdfs -> case FDF.mapForms ON.returnFields ON.fixReturns (Map.fromList $ zip (formKey <$> fdfFileNames) fdfs) of
      Left err -> die err
      Right filled
        | let fdfOutputs = fromStrict . serialize <$> filled
              diff ref new = ["diff", "-uw", ref, new]
              compare key fdfOutput
                | let Just fileName = List.find ((key ==) . formKey) fdfFileNames
                = pure $ goldenVsStringDiff fileName diff (combine referenceDir $ combine path fileName) (pure fdfOutput)
        -> testGroup path . toList <$> Map.traverseWithKey compare fdfOutputs


formKey :: FilePath -> Text
formKey "5000-s6-fill-23e.fdf" = "Schedule6"
formKey "5006-c-fill-23e.fdf" = "428"
formKey "5006-r-fill-23e.fdf" = "T1"
formKey name = error ("File name " <> name <> " is not recognized as a form.")