module Network.CloudSeeder.MainSpec (spec) where import Control.Lens (review) import Control.Monad.Except (ExceptT, runExceptT) import Data.Function ((&)) import Data.Functor.Identity (runIdentity) import Test.Hspec import Network.CloudSeeder.DSL import Network.CloudSeeder.Interfaces import Network.CloudSeeder.Main import Network.CloudSeeder.Test.Stubs spec :: Spec spec = parallel $ do describe "cli" $ do let stubExceptT :: ExceptT CliError m a -> m (Either CliError a) stubExceptT = runExceptT runSuccess x = runIdentity x `shouldBe` Right () runFailure p y x = runIdentity x `shouldBe` Left (review p y) it "fails if the template doesn't exist" $ do let config = runIdentity $ deployment "foo" $ do stack_ "base" stack_ "server" env = [("Env", "test")] runFailure _FileNotFound "server.yaml" $ cli (DeployStack "server") config & stubFileSystemT [] & stubExceptT & stubEnvironmentT env & stubCloudT [] it "fails if user attempts to deploy a stack that doesn't exist in the config" $ do let config = runIdentity $ deployment "foo" $ do stack_ "base" env = [("Env", "test")] runFailure _CliStackNotConfigured "foo" $ cli (DeployStack "foo") config & stubFileSystemT [ ("base.yaml", "base.yaml contents")] & stubExceptT & stubEnvironmentT env & stubCloudT [] context "the configuration does not have environment variables" $ do let config = runIdentity $ deployment "foo" $ do stack_ "base" stack_ "server" stack_ "frontend" env = [("Env", "test")] it "applies a changeset to a stack" $ example $ do runSuccess $ cli (DeployStack "base") config & stubFileSystemT [ ("base.yaml", "base.yaml contents") ] & stubExceptT & stubEnvironmentT env & stubCloudT [ ComputeChangeset "test-foo-base" "base.yaml contents" env :-> "csid" , RunChangeSet "csid" :-> () ] it "passes the outputs from prior stacks" $ do runSuccess $ cli (DeployStack "server") config & stubFileSystemT [ ("server.yaml", "server.yaml contents") ] & stubExceptT & stubEnvironmentT env & stubCloudT [ DescribeStack "test-foo-base" :-> Just [("foo", "bar")] , ComputeChangeset "test-foo-server" "server.yaml contents" (env ++ [("foo", "bar")]) :-> "csid" , RunChangeSet "csid" :-> () ] runSuccess $ cli (DeployStack "frontend") config & stubFileSystemT [ ("frontend.yaml", "frontend.yaml contents") ] & stubExceptT & stubEnvironmentT env & stubCloudT [ DescribeStack "test-foo-base" :-> Just [("foo", "bar")] , DescribeStack "test-foo-server" :-> Just [("baz", "qux")] , ComputeChangeset "test-foo-frontend" "frontend.yaml contents" (env ++ [("foo", "bar"), ("baz", "qux")]) :-> "csid" , RunChangeSet "csid" :-> () ] it "fails if a dependency stack does not exist" $ do runFailure _CliMissingDependencyStacks ["base"] $ cli (DeployStack "frontend") config & stubFileSystemT [ ("frontend.yaml", "frontend.yaml contents") ] & stubExceptT & stubEnvironmentT env & stubCloudT [ DescribeStack "test-foo-base" :-> Nothing , DescribeStack "test-foo-server" :-> Just [] ] runFailure _CliMissingDependencyStacks ["server"] $ cli (DeployStack "frontend") config & stubFileSystemT [ ("frontend.yaml", "frontend.yaml contents") ] & stubExceptT & stubEnvironmentT env & stubCloudT [ DescribeStack "test-foo-base" :-> Just [] , DescribeStack "test-foo-server" :-> Nothing ] runFailure _CliMissingDependencyStacks ["base", "server"] $ cli (DeployStack "frontend") config & stubFileSystemT [ ("frontend.yaml", "frontend.yaml contents") ] & stubExceptT & stubEnvironmentT env & stubCloudT [ DescribeStack "test-foo-base" :-> Nothing , DescribeStack "test-foo-server" :-> Nothing ] it "fails when the Env environment variable is not specified" $ do runFailure _CliMissingEnvVars ["Env"] $ cli (DeployStack "server") config & stubFileSystemT [ ("server.yaml", "server.yaml contents") ] & stubExceptT & stubEnvironmentT [] & stubCloudT [] context "the configuration has global environment variables" $ do let config = runIdentity $ deployment "foo" $ do environment ["Domain", "SecretsStore"] stack_ "base" stack_ "server" stack_ "frontend" it "passes the value in each global environment variable as a parameter" $ do let env = [ ("Env", "test"), ("Domain", "example.com"), ("SecretsStore", "arn::aws:1234") ] runSuccess $ cli (DeployStack "base") config & stubFileSystemT [ ("base.yaml", "base.yaml contents") ] & stubExceptT & stubEnvironmentT env & stubCloudT [ ComputeChangeset "test-foo-base" "base.yaml contents" env :-> "csid" , RunChangeSet "csid" :-> () ] it "fails when a global environment variable is missing" $ do let env = [ ("Env", "test"), ("SecretsStore", "arn::aws:1234") ] runFailure _CliMissingEnvVars ["Domain"] $ cli (DeployStack "base") config & stubFileSystemT [ ("base.yaml", "base.yaml contents") ] & stubExceptT & stubEnvironmentT env & stubCloudT [] it "reports all missing environment variables at once in alphabetical order" $ do runFailure _CliMissingEnvVars ["Domain", "Env", "SecretsStore"] $ cli (DeployStack "base") config & stubFileSystemT [ ("base.yaml", "base.yaml contents") ] & stubExceptT & stubEnvironmentT [] & stubCloudT [] context "the configuration has global and local environment variables" $ do let config = runIdentity $ deployment "foo" $ do environment ["Domain", "SecretsStore"] stack "base" $ environment ["Base"] stack "server" $ environment ["Server1", "Server2"] stack "frontend" $ environment ["Frontend"] it "passes the value in each local environment variable to the proper stack" $ do let env = [ ("Env", "test"), ("Domain", "example.com"), ("SecretsStore", "arn::aws:1234") ] baseEnv = env ++ [ ("Base", "a") ] runSuccess $ cli (DeployStack "base") config & stubFileSystemT [ ("base.yaml", "base.yaml contents") ] & stubExceptT & stubEnvironmentT baseEnv & stubCloudT [ ComputeChangeset "test-foo-base" "base.yaml contents" baseEnv :-> "csid" , RunChangeSet "csid" :-> () ] let serverEnv = env ++ [ ("Server1", "b"), ("Server2", "c") ] runSuccess $ cli (DeployStack "server") config & stubFileSystemT [ ("server.yaml", "server.yaml contents") ] & stubExceptT & stubEnvironmentT serverEnv & stubCloudT [ DescribeStack "test-foo-base" :-> Just [] , ComputeChangeset "test-foo-server" "server.yaml contents" serverEnv :-> "csid" , RunChangeSet "csid" :-> () ]