From f68f5b603e54f67bf2e99d217d2bf521b1d0d778 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sat, 13 Jul 2024 22:50:00 +0200 Subject: [PATCH 01/21] Save only the relative path of the filename for `StaticDirStorage` fixes https://github.com/digitallyinduced/ihp/issues/1989 --- IHP/FileStorage/ControllerFunctions.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IHP/FileStorage/ControllerFunctions.hs b/IHP/FileStorage/ControllerFunctions.hs index 864b9a74e..a0c75f80c 100644 --- a/IHP/FileStorage/ControllerFunctions.hs +++ b/IHP/FileStorage/ControllerFunctions.hs @@ -112,7 +112,7 @@ storeFileWithOptions fileInfo options = do |> LBS.writeFile (cs destPath) let frameworkConfig = ?context.frameworkConfig - pure $ frameworkConfig.baseUrl <> "/" <> objectPath + pure $ "/" <> objectPath S3Storage { connectInfo, bucket, baseUrl } -> do let payload = fileInfo |> (.fileContent) @@ -409,4 +409,4 @@ storage = ?context.frameworkConfig.appConfig storagePrefix :: (?context :: ControllerContext) => Text storagePrefix = case storage of StaticDirStorage -> "static/" - _ -> "" \ No newline at end of file + _ -> "" From 6e0db34f22b5f604e20b610eb707a4c6b8f23d84 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sat, 5 Oct 2024 20:19:46 +0300 Subject: [PATCH 02/21] Adapt createTemporaryDownloadUrlFromPathWithExpiredAt --- IHP/FileStorage/ControllerFunctions.hs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/IHP/FileStorage/ControllerFunctions.hs b/IHP/FileStorage/ControllerFunctions.hs index a0c75f80c..0c091a062 100644 --- a/IHP/FileStorage/ControllerFunctions.hs +++ b/IHP/FileStorage/ControllerFunctions.hs @@ -226,7 +226,13 @@ createTemporaryDownloadUrlFromPathWithExpiredAt validInSeconds objectPath = do case storage of StaticDirStorage -> do let frameworkConfig = ?context.frameworkConfig - let url = frameworkConfig.baseUrl <> "/" <> objectPath + let urlSchemes = ["http://", "https://"] + + let url = if any (`isPrefixOf` objectPath) urlSchemes + -- BC, before we saved only the relative path of a file, we saved the full URL. So use it as is. + then objectPath + -- We have the relative path, so add the baseUrl. + else frameworkConfig.baseUrl <> objectPath pure TemporaryDownloadUrl { url = cs url, expiredAt = publicUrlExpiredAt } S3Storage { connectInfo, bucket} -> do From d5865c4cdd3fcc130cbbb0efc53cceda793a4dce Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sat, 5 Oct 2024 20:31:50 +0300 Subject: [PATCH 03/21] Change slash logic --- IHP/FileStorage/ControllerFunctions.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/IHP/FileStorage/ControllerFunctions.hs b/IHP/FileStorage/ControllerFunctions.hs index 0c091a062..06450652c 100644 --- a/IHP/FileStorage/ControllerFunctions.hs +++ b/IHP/FileStorage/ControllerFunctions.hs @@ -112,7 +112,7 @@ storeFileWithOptions fileInfo options = do |> LBS.writeFile (cs destPath) let frameworkConfig = ?context.frameworkConfig - pure $ "/" <> objectPath + pure $ objectPath S3Storage { connectInfo, bucket, baseUrl } -> do let payload = fileInfo |> (.fileContent) @@ -230,9 +230,9 @@ createTemporaryDownloadUrlFromPathWithExpiredAt validInSeconds objectPath = do let url = if any (`isPrefixOf` objectPath) urlSchemes -- BC, before we saved only the relative path of a file, we saved the full URL. So use it as is. - then objectPath + then "/" <> objectPath -- We have the relative path, so add the baseUrl. - else frameworkConfig.baseUrl <> objectPath + else frameworkConfig.baseUrl <> "/" <> objectPath pure TemporaryDownloadUrl { url = cs url, expiredAt = publicUrlExpiredAt } S3Storage { connectInfo, bucket} -> do From b9ced4439b3b5e73cbe7532fa74a07768c404e75 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sat, 5 Oct 2024 20:35:22 +0300 Subject: [PATCH 04/21] Start adding tests --- Test/FileStorage/ControllerFunctionsSpec.hs | 89 +++++++++++++++++++++ Test/Main.hs | 2 + 2 files changed, 91 insertions(+) create mode 100644 Test/FileStorage/ControllerFunctionsSpec.hs diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs new file mode 100644 index 000000000..a8def970c --- /dev/null +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -0,0 +1,89 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ImplicitParams #-} + +module Test.FileStorage.ControllerFunctionsSpec (spec) where + +import Test.Hspec +import IHP.Prelude +import IHP.FileStorage.ControllerFunctions +import IHP.Controller.Context +import IHP.FrameworkConfig +import IHP.ModelSupport +import IHP.FileStorage.Types +import System.IO.Temp (withSystemTempDirectory) +import qualified Data.ByteString.Lazy as LBS +import qualified Data.Text as Text +import Data.Default (def) +import Data.Time.Clock (getCurrentTime, addUTCTime) +import IHP.Controller.RequestContext (RequestContext (..)) +import Network.Wai (defaultRequest) + +spec :: Spec +spec = describe "IHP.FileStorage.ControllerFunctions" $ do + describe "storeFileWithOptions" $ do + it "returns the objectPath without the baseUrl" $ do + withSystemTempDirectory "ihp-test" $ \tempDir -> do + let frameworkConfig = def + { baseUrl = "http://localhost:8000" + , staticDir = tempDir + , storageDir = tempDir + , appConfig = StaticDirStorage + } + let ?context = ControllerContext + { requestContext = RequestContext + { request = defaultRequest + , respond = \_ -> pure undefined + , vault = mempty + , frameworkConfig = frameworkConfig + , requestBody = pure "" + } + , response = error "response not used in test" + , applicationContext = error "applicationContext not used in test" + } + let fileInfo = FileInfo + { fileName = "test.txt" + , contentType = "text/plain" + , fileContent = "Hello, world!" + } + let options = def { objectName = Just "test.txt" } + + result <- storeFileWithOptions fileInfo options + + result `shouldBe` "static/test.txt" + + describe "createTemporaryDownloadUrlFromPathWithExpiredAt" $ do + it "returns baseUrl concatenated with objectPath when objectPath does not start with http:// or https://" $ do + let frameworkConfig = def { baseUrl = "http://localhost:8000", appConfig = StaticDirStorage } + let ?context = ControllerContext + { requestContext = RequestContext + { request = defaultRequest + , respond = \_ -> pure undefined + , vault = mempty + , frameworkConfig = frameworkConfig + , requestBody = pure "" + } + , response = error "response not used in test" + , applicationContext = error "applicationContext not used in test" + } + let objectPath = "static/test.txt" + temporaryDownloadUrl <- createTemporaryDownloadUrlFromPathWithExpiredAt 3600 objectPath + + temporaryDownloadUrl.url `shouldBe` "http://localhost:8000/static/test.txt" + + it "returns '/' concatenated with objectPath when objectPath starts with 'http://' or 'https://'" $ do + let frameworkConfig = def { baseUrl = "http://localhost:8000", appConfig = StaticDirStorage } + let ?context = ControllerContext + { requestContext = RequestContext + { request = defaultRequest + , respond = \_ -> pure undefined + , vault = mempty + , frameworkConfig = frameworkConfig + , requestBody = pure "" + } + , response = error "response not used in test" + , applicationContext = error "applicationContext not used in test" + } + let objectPath = "https://example.com/static/test.txt" + temporaryDownloadUrl <- createTemporaryDownloadUrlFromPathWithExpiredAt 3600 objectPath + + temporaryDownloadUrl.url `shouldBe` "https://example.com/static/test.txt" diff --git a/Test/Main.hs b/Test/Main.hs index 4a26b94cd..7a03cfb2c 100644 --- a/Test/Main.hs +++ b/Test/Main.hs @@ -44,6 +44,7 @@ import qualified Test.RouterSupportSpec import qualified Test.ViewSupportSpec import qualified Test.ServerSideComponent.HtmlParserSpec import qualified Test.ServerSideComponent.HtmlDiffSpec +import qualified Test.FileStorage.ControllerFunctionsSpec import qualified Test.FileStorage.MimeTypesSpec import qualified Test.DataSync.DynamicQueryCompiler import qualified Test.IDE.CodeGeneration.MigrationGenerator @@ -78,6 +79,7 @@ main = hspec do Test.ViewSupportSpec.tests Test.ServerSideComponent.HtmlParserSpec.tests Test.ServerSideComponent.HtmlDiffSpec.tests + Test.FileStorage.ControllerFunctionsSpec.tests Test.FileStorage.MimeTypesSpec.tests Test.DataSync.DynamicQueryCompiler.tests Test.IDE.SchemaDesigner.SchemaOperationsSpec.tests From 73c207479eb63abaae20b3ff13abadbf20736801 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 07:44:06 +0300 Subject: [PATCH 05/21] Use newControllerContext --- Test/FileStorage/ControllerFunctionsSpec.hs | 46 +++------------------ 1 file changed, 5 insertions(+), 41 deletions(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index a8def970c..4c2015f71 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -23,23 +23,9 @@ spec = describe "IHP.FileStorage.ControllerFunctions" $ do describe "storeFileWithOptions" $ do it "returns the objectPath without the baseUrl" $ do withSystemTempDirectory "ihp-test" $ \tempDir -> do - let frameworkConfig = def - { baseUrl = "http://localhost:8000" - , staticDir = tempDir - , storageDir = tempDir - , appConfig = StaticDirStorage - } - let ?context = ControllerContext - { requestContext = RequestContext - { request = defaultRequest - , respond = \_ -> pure undefined - , vault = mempty - , frameworkConfig = frameworkConfig - , requestBody = pure "" - } - , response = error "response not used in test" - , applicationContext = error "applicationContext not used in test" - } + context <- newControllerContext + let ?context = context + let fileInfo = FileInfo { fileName = "test.txt" , contentType = "text/plain" @@ -53,36 +39,14 @@ spec = describe "IHP.FileStorage.ControllerFunctions" $ do describe "createTemporaryDownloadUrlFromPathWithExpiredAt" $ do it "returns baseUrl concatenated with objectPath when objectPath does not start with http:// or https://" $ do - let frameworkConfig = def { baseUrl = "http://localhost:8000", appConfig = StaticDirStorage } - let ?context = ControllerContext - { requestContext = RequestContext - { request = defaultRequest - , respond = \_ -> pure undefined - , vault = mempty - , frameworkConfig = frameworkConfig - , requestBody = pure "" - } - , response = error "response not used in test" - , applicationContext = error "applicationContext not used in test" - } + context <- newControllerContext let objectPath = "static/test.txt" temporaryDownloadUrl <- createTemporaryDownloadUrlFromPathWithExpiredAt 3600 objectPath temporaryDownloadUrl.url `shouldBe` "http://localhost:8000/static/test.txt" it "returns '/' concatenated with objectPath when objectPath starts with 'http://' or 'https://'" $ do - let frameworkConfig = def { baseUrl = "http://localhost:8000", appConfig = StaticDirStorage } - let ?context = ControllerContext - { requestContext = RequestContext - { request = defaultRequest - , respond = \_ -> pure undefined - , vault = mempty - , frameworkConfig = frameworkConfig - , requestBody = pure "" - } - , response = error "response not used in test" - , applicationContext = error "applicationContext not used in test" - } + context <- newControllerContext let objectPath = "https://example.com/static/test.txt" temporaryDownloadUrl <- createTemporaryDownloadUrlFromPathWithExpiredAt 3600 objectPath From 6702d3eae6b14658918dbf5e09f12d30857526b0 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 07:56:49 +0300 Subject: [PATCH 06/21] Start fix tests --- Test/FileStorage/ControllerFunctionsSpec.hs | 41 ++++++++++----------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index 4c2015f71..6bc30dd45 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -1,7 +1,4 @@ -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ImplicitParams #-} - -module Test.FileStorage.ControllerFunctionsSpec (spec) where +module Test.FileStorage.ControllerFunctionsSpec where import Test.Hspec import IHP.Prelude @@ -15,11 +12,12 @@ import qualified Data.ByteString.Lazy as LBS import qualified Data.Text as Text import Data.Default (def) import Data.Time.Clock (getCurrentTime, addUTCTime) -import IHP.Controller.RequestContext (RequestContext (..)) import Network.Wai (defaultRequest) +import Network.Wai.Parse (FileInfo(..)) -spec :: Spec -spec = describe "IHP.FileStorage.ControllerFunctions" $ do +tests :: Spec +tests = describe "IHP.FileStorage.ControllerFunctions" $ do + let ?requestContext = undefined describe "storeFileWithOptions" $ do it "returns the objectPath without the baseUrl" $ do withSystemTempDirectory "ihp-test" $ \tempDir -> do @@ -28,26 +26,25 @@ spec = describe "IHP.FileStorage.ControllerFunctions" $ do let fileInfo = FileInfo { fileName = "test.txt" - , contentType = "text/plain" + , fileContentType = "text/plain" , fileContent = "Hello, world!" } - let options = def { objectName = Just "test.txt" } - result <- storeFileWithOptions fileInfo options + result <- storeFile fileInfo "Test.FileStorage.ControllerFunctionsSpec" - result `shouldBe` "static/test.txt" + result.url `shouldBe` "Test.FileStorage.ControllerFunctionsSpec/test.txt" - describe "createTemporaryDownloadUrlFromPathWithExpiredAt" $ do - it "returns baseUrl concatenated with objectPath when objectPath does not start with http:// or https://" $ do - context <- newControllerContext - let objectPath = "static/test.txt" - temporaryDownloadUrl <- createTemporaryDownloadUrlFromPathWithExpiredAt 3600 objectPath + -- describe "createTemporaryDownloadUrlFromPathWithExpiredAt" $ do + -- it "returns baseUrl concatenated with objectPath when objectPath does not start with http:// or https://" $ do + -- context <- newControllerContext + -- let objectPath = "static/test.txt" + -- temporaryDownloadUrl <- createTemporaryDownloadUrlFromPathWithExpiredAt 3600 objectPath - temporaryDownloadUrl.url `shouldBe` "http://localhost:8000/static/test.txt" + -- temporaryDownloadUrl.url `shouldBe` "http://localhost:8000/static/test.txt" - it "returns '/' concatenated with objectPath when objectPath starts with 'http://' or 'https://'" $ do - context <- newControllerContext - let objectPath = "https://example.com/static/test.txt" - temporaryDownloadUrl <- createTemporaryDownloadUrlFromPathWithExpiredAt 3600 objectPath + -- it "returns '/' concatenated with objectPath when objectPath starts with 'http://' or 'https://'" $ do + -- context <- newControllerContext + -- let objectPath = "https://example.com/static/test.txt" + -- temporaryDownloadUrl <- createTemporaryDownloadUrlFromPathWithExpiredAt 3600 objectPath - temporaryDownloadUrl.url `shouldBe` "https://example.com/static/test.txt" + -- temporaryDownloadUrl.url `shouldBe` "https://example.com/static/test.txt" From 2aa73c0af90ddb428cc32a24d053e59c00fee0c8 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 07:59:42 +0300 Subject: [PATCH 07/21] More fixes --- Test/FileStorage/ControllerFunctionsSpec.hs | 30 +++++++++++---------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index 6bc30dd45..1048080a0 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -34,17 +34,19 @@ tests = describe "IHP.FileStorage.ControllerFunctions" $ do result.url `shouldBe` "Test.FileStorage.ControllerFunctionsSpec/test.txt" - -- describe "createTemporaryDownloadUrlFromPathWithExpiredAt" $ do - -- it "returns baseUrl concatenated with objectPath when objectPath does not start with http:// or https://" $ do - -- context <- newControllerContext - -- let objectPath = "static/test.txt" - -- temporaryDownloadUrl <- createTemporaryDownloadUrlFromPathWithExpiredAt 3600 objectPath - - -- temporaryDownloadUrl.url `shouldBe` "http://localhost:8000/static/test.txt" - - -- it "returns '/' concatenated with objectPath when objectPath starts with 'http://' or 'https://'" $ do - -- context <- newControllerContext - -- let objectPath = "https://example.com/static/test.txt" - -- temporaryDownloadUrl <- createTemporaryDownloadUrlFromPathWithExpiredAt 3600 objectPath - - -- temporaryDownloadUrl.url `shouldBe` "https://example.com/static/test.txt" + describe "createTemporaryDownloadUrlFromPathWithExpiredAt" $ do + it "returns baseUrl concatenated with objectPath when objectPath does not start with http:// or https://" $ do + context <- newControllerContext + let ?context = context + let objectPath = "static/test.txt" + temporaryDownloadUrl <- createTemporaryDownloadUrlFromPath objectPath + + temporaryDownloadUrl.url `shouldBe` "http://localhost:8000/static/test.txt" + + it "returns '/' concatenated with objectPath when objectPath starts with 'http://' or 'https://'" $ do + context <- newControllerContext + let ?context = context + let objectPath = "https://example.com/static/test.txt" + temporaryDownloadUrl <- createTemporaryDownloadUrlFromPath objectPath + + temporaryDownloadUrl.url `shouldBe` "https://example.com/static/test.txt" From ac3f57f5c0a81593c67f4eed0dde2cb2020e45b0 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 08:03:24 +0300 Subject: [PATCH 08/21] Copy from another test --- Test/FileStorage/ControllerFunctionsSpec.hs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index 1048080a0..bfb00fa0a 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -12,16 +12,16 @@ import qualified Data.ByteString.Lazy as LBS import qualified Data.Text as Text import Data.Default (def) import Data.Time.Clock (getCurrentTime, addUTCTime) -import Network.Wai (defaultRequest) +import Network.Wai as Wai (defaultRequest) import Network.Wai.Parse (FileInfo(..)) +import IHP.Controller.RequestContext tests :: Spec tests = describe "IHP.FileStorage.ControllerFunctions" $ do - let ?requestContext = undefined describe "storeFileWithOptions" $ do it "returns the objectPath without the baseUrl" $ do withSystemTempDirectory "ihp-test" $ \tempDir -> do - context <- newControllerContext + context <- createControllerContext let ?context = context let fileInfo = FileInfo @@ -36,7 +36,7 @@ tests = describe "IHP.FileStorage.ControllerFunctions" $ do describe "createTemporaryDownloadUrlFromPathWithExpiredAt" $ do it "returns baseUrl concatenated with objectPath when objectPath does not start with http:// or https://" $ do - context <- newControllerContext + context <- createControllerContext let ?context = context let objectPath = "static/test.txt" temporaryDownloadUrl <- createTemporaryDownloadUrlFromPath objectPath @@ -44,9 +44,17 @@ tests = describe "IHP.FileStorage.ControllerFunctions" $ do temporaryDownloadUrl.url `shouldBe` "http://localhost:8000/static/test.txt" it "returns '/' concatenated with objectPath when objectPath starts with 'http://' or 'https://'" $ do - context <- newControllerContext + context <- createControllerContext let ?context = context let objectPath = "https://example.com/static/test.txt" temporaryDownloadUrl <- createTemporaryDownloadUrlFromPath objectPath temporaryDownloadUrl.url `shouldBe` "https://example.com/static/test.txt" + +createControllerContext = do + let + requestBody = FormBody { params = [], files = [] } + request = Wai.defaultRequest + requestContext = RequestContext { request, respond = error "respond", requestBody, frameworkConfig = error "frameworkConfig" } + let ?requestContext = requestContext + newControllerContext \ No newline at end of file From 04c8a65a3791ca79811d3b54c13c06532dbac091 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 20:30:42 +0300 Subject: [PATCH 09/21] Add withFrameworkConfig --- Test/FileStorage/ControllerFunctionsSpec.hs | 48 +++++++++++---------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index bfb00fa0a..8c91b08b0 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -18,43 +18,47 @@ import IHP.Controller.RequestContext tests :: Spec tests = describe "IHP.FileStorage.ControllerFunctions" $ do + let withFrameworkConfig = IHP.FrameworkConfig.withFrameworkConfig (pure ()) describe "storeFileWithOptions" $ do it "returns the objectPath without the baseUrl" $ do withSystemTempDirectory "ihp-test" $ \tempDir -> do - context <- createControllerContext - let ?context = context + withFrameworkConfig \frameworkConfig -> do + context <- createControllerContext frameworkConfig + let ?context = context - let fileInfo = FileInfo - { fileName = "test.txt" - , fileContentType = "text/plain" - , fileContent = "Hello, world!" - } + let fileInfo = FileInfo + { fileName = "test.txt" + , fileContentType = "text/plain" + , fileContent = "Hello, world!" + } - result <- storeFile fileInfo "Test.FileStorage.ControllerFunctionsSpec" + result <- storeFile fileInfo "Test.FileStorage.ControllerFunctionsSpec" - result.url `shouldBe` "Test.FileStorage.ControllerFunctionsSpec/test.txt" + result.url `shouldBe` "Test.FileStorage.ControllerFunctionsSpec/test.txt" - describe "createTemporaryDownloadUrlFromPathWithExpiredAt" $ do + describe "createTemporaryDownloadUrlFromPath" $ do it "returns baseUrl concatenated with objectPath when objectPath does not start with http:// or https://" $ do - context <- createControllerContext - let ?context = context - let objectPath = "static/test.txt" - temporaryDownloadUrl <- createTemporaryDownloadUrlFromPath objectPath + withFrameworkConfig \frameworkConfig -> do + context <- createControllerContext frameworkConfig + let ?context = context + let objectPath = "static/test.txt" + temporaryDownloadUrl <- createTemporaryDownloadUrlFromPath objectPath - temporaryDownloadUrl.url `shouldBe` "http://localhost:8000/static/test.txt" + temporaryDownloadUrl.url `shouldBe` "http://localhost:8000/static/test.txt" it "returns '/' concatenated with objectPath when objectPath starts with 'http://' or 'https://'" $ do - context <- createControllerContext - let ?context = context - let objectPath = "https://example.com/static/test.txt" - temporaryDownloadUrl <- createTemporaryDownloadUrlFromPath objectPath + withFrameworkConfig \frameworkConfig -> do + context <- createControllerContext frameworkConfig + let ?context = context + let objectPath = "https://example.com/static/test.txt" + temporaryDownloadUrl <- createTemporaryDownloadUrlFromPath objectPath - temporaryDownloadUrl.url `shouldBe` "https://example.com/static/test.txt" + temporaryDownloadUrl.url `shouldBe` "https://example.com/static/test.txt" -createControllerContext = do +createControllerContext frameworkConfig = do let requestBody = FormBody { params = [], files = [] } request = Wai.defaultRequest - requestContext = RequestContext { request, respond = error "respond", requestBody, frameworkConfig = error "frameworkConfig" } + requestContext = RequestContext { request, respond = error "respond", requestBody, frameworkConfig = frameworkConfig } let ?requestContext = requestContext newControllerContext \ No newline at end of file From 600d9feae61ff75157d6eec80735b894c1a8aff1 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 20:55:52 +0300 Subject: [PATCH 10/21] Fix compile --- Test/FileStorage/ControllerFunctionsSpec.hs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index 8c91b08b0..1f06694e2 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -15,10 +15,20 @@ import Data.Time.Clock (getCurrentTime, addUTCTime) import Network.Wai as Wai (defaultRequest) import Network.Wai.Parse (FileInfo(..)) import IHP.Controller.RequestContext +import IHP.FileStorage.Config +import qualified IHP.FrameworkConfig as Config + tests :: Spec tests = describe "IHP.FileStorage.ControllerFunctions" $ do - let withFrameworkConfig = IHP.FrameworkConfig.withFrameworkConfig (pure ()) + + let config :: ConfigBuilder + config = do + initStaticDirStorage + + let withFrameworkConfig = IHP.FrameworkConfig.withFrameworkConfig config + + describe "storeFileWithOptions" $ do it "returns the objectPath without the baseUrl" $ do withSystemTempDirectory "ihp-test" $ \tempDir -> do @@ -61,4 +71,8 @@ createControllerContext frameworkConfig = do request = Wai.defaultRequest requestContext = RequestContext { request, respond = error "respond", requestBody, frameworkConfig = frameworkConfig } let ?requestContext = requestContext - newControllerContext \ No newline at end of file + newControllerContext + + + + From 2c867f44f9c4fe759d0894af0061519483776df8 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 20:58:53 +0300 Subject: [PATCH 11/21] Fix tests --- Test/Main.hs | 66 ++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Test/Main.hs b/Test/Main.hs index 7a03cfb2c..d324136fc 100644 --- a/Test/Main.hs +++ b/Test/Main.hs @@ -53,37 +53,37 @@ import qualified Test.SEO.Sitemap main :: IO () main = hspec do - Test.IDE.SchemaDesigner.CompilerSpec.tests - Test.IDE.SchemaDesigner.ParserSpec.tests - Test.IDE.SchemaDesigner.Controller.EnumValuesSpec.tests - Test.IDE.SchemaDesigner.Controller.HelperSpec.tests - Test.IDE.SchemaDesigner.Controller.ValidationSpec.tests - Test.ValidationSupport.ValidateFieldSpec.tests - Test.IDE.CodeGeneration.ControllerGenerator.tests - Test.IDE.CodeGeneration.ViewGenerator.tests - Test.IDE.CodeGeneration.MailGenerator.tests - Test.IDE.CodeGeneration.JobGenerator.tests - Test.NameSupportSpec.tests - Test.HaskellSupportSpec.tests - Test.View.CSSFrameworkSpec.tests - Test.View.FormSpec.tests - Test.Controller.ContextSpec.tests - Test.Controller.ParamSpec.tests - Test.Controller.AccessDeniedSpec.tests - Test.Controller.NotFoundSpec.tests - Test.SchemaMigrationSpec.tests - Test.ModelSupportSpec.tests - Test.SchemaCompilerSpec.tests - Test.QueryBuilderSpec.tests - Test.RouterSupportSpec.tests - Test.ViewSupportSpec.tests - Test.ServerSideComponent.HtmlParserSpec.tests - Test.ServerSideComponent.HtmlDiffSpec.tests + -- Test.IDE.SchemaDesigner.CompilerSpec.tests + -- Test.IDE.SchemaDesigner.ParserSpec.tests + -- Test.IDE.SchemaDesigner.Controller.EnumValuesSpec.tests + -- Test.IDE.SchemaDesigner.Controller.HelperSpec.tests + -- Test.IDE.SchemaDesigner.Controller.ValidationSpec.tests + -- Test.ValidationSupport.ValidateFieldSpec.tests + -- Test.IDE.CodeGeneration.ControllerGenerator.tests + -- Test.IDE.CodeGeneration.ViewGenerator.tests + -- Test.IDE.CodeGeneration.MailGenerator.tests + -- Test.IDE.CodeGeneration.JobGenerator.tests + -- Test.NameSupportSpec.tests + -- Test.HaskellSupportSpec.tests + -- Test.View.CSSFrameworkSpec.tests + -- Test.View.FormSpec.tests + -- Test.Controller.ContextSpec.tests + -- Test.Controller.ParamSpec.tests + -- Test.Controller.AccessDeniedSpec.tests + -- Test.Controller.NotFoundSpec.tests + -- Test.SchemaMigrationSpec.tests + -- Test.ModelSupportSpec.tests + -- Test.SchemaCompilerSpec.tests + -- Test.QueryBuilderSpec.tests + -- Test.RouterSupportSpec.tests + -- Test.ViewSupportSpec.tests + -- Test.ServerSideComponent.HtmlParserSpec.tests + -- Test.ServerSideComponent.HtmlDiffSpec.tests Test.FileStorage.ControllerFunctionsSpec.tests - Test.FileStorage.MimeTypesSpec.tests - Test.DataSync.DynamicQueryCompiler.tests - Test.IDE.SchemaDesigner.SchemaOperationsSpec.tests - Test.IDE.CodeGeneration.MigrationGenerator.tests - Test.Controller.CookieSpec.tests - Test.PGListenerSpec.tests - Test.SEO.Sitemap.tests + -- Test.FileStorage.MimeTypesSpec.tests + -- Test.DataSync.DynamicQueryCompiler.tests + -- Test.IDE.SchemaDesigner.SchemaOperationsSpec.tests + -- Test.IDE.CodeGeneration.MigrationGenerator.tests + -- Test.Controller.CookieSpec.tests + -- Test.PGListenerSpec.tests + -- Test.SEO.Sitemap.tests From 311e57d69ab7a1f6730b110caf47559eeed9374e Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 20:59:01 +0300 Subject: [PATCH 12/21] Fix logic --- IHP/FileStorage/ControllerFunctions.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IHP/FileStorage/ControllerFunctions.hs b/IHP/FileStorage/ControllerFunctions.hs index 06450652c..9ae9dab88 100644 --- a/IHP/FileStorage/ControllerFunctions.hs +++ b/IHP/FileStorage/ControllerFunctions.hs @@ -230,7 +230,7 @@ createTemporaryDownloadUrlFromPathWithExpiredAt validInSeconds objectPath = do let url = if any (`isPrefixOf` objectPath) urlSchemes -- BC, before we saved only the relative path of a file, we saved the full URL. So use it as is. - then "/" <> objectPath + then objectPath -- We have the relative path, so add the baseUrl. else frameworkConfig.baseUrl <> "/" <> objectPath From 35af26fa94faace1720a50c33b509afdfc773f84 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 21:00:39 +0300 Subject: [PATCH 13/21] Ignore test folders --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index e8d960f41..548b8c738 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ devenv.local.nix result* .idea + +# Test folders +static/Test.FileStorage.ControllerFunctionsSpec \ No newline at end of file From 8ecb835a5b69018f1dbc5f81dd8d9d8c8f890460 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 21:11:40 +0300 Subject: [PATCH 14/21] Fix tests --- Test/FileStorage/ControllerFunctionsSpec.hs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index 1f06694e2..e85a80678 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -42,9 +42,15 @@ tests = describe "IHP.FileStorage.ControllerFunctions" $ do , fileContent = "Hello, world!" } - result <- storeFile fileInfo "Test.FileStorage.ControllerFunctionsSpec" + -- We pass the UUID that will be used as the filename, so we can easily assert the objectPath. + let options :: StoreFileOptions = def + { fileName = Just "4c55dac2-e411-45ac-aa10-b957b01221df" + , directory = "Test.FileStorage.ControllerFunctionsSpec" + } + + result <- storeFileWithOptions fileInfo options - result.url `shouldBe` "Test.FileStorage.ControllerFunctionsSpec/test.txt" + result.url `shouldBe` ("Test.FileStorage.ControllerFunctionsSpec/4c55dac2-e411-45ac-aa10-b957b01221df") describe "createTemporaryDownloadUrlFromPath" $ do it "returns baseUrl concatenated with objectPath when objectPath does not start with http:// or https://" $ do From 1af1f4f4d54ea0e68e54fad2c026d860fbcce4bd Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 21:11:55 +0300 Subject: [PATCH 15/21] Re-enable all tests --- Test/Main.hs | 66 ++++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/Test/Main.hs b/Test/Main.hs index d324136fc..7a03cfb2c 100644 --- a/Test/Main.hs +++ b/Test/Main.hs @@ -53,37 +53,37 @@ import qualified Test.SEO.Sitemap main :: IO () main = hspec do - -- Test.IDE.SchemaDesigner.CompilerSpec.tests - -- Test.IDE.SchemaDesigner.ParserSpec.tests - -- Test.IDE.SchemaDesigner.Controller.EnumValuesSpec.tests - -- Test.IDE.SchemaDesigner.Controller.HelperSpec.tests - -- Test.IDE.SchemaDesigner.Controller.ValidationSpec.tests - -- Test.ValidationSupport.ValidateFieldSpec.tests - -- Test.IDE.CodeGeneration.ControllerGenerator.tests - -- Test.IDE.CodeGeneration.ViewGenerator.tests - -- Test.IDE.CodeGeneration.MailGenerator.tests - -- Test.IDE.CodeGeneration.JobGenerator.tests - -- Test.NameSupportSpec.tests - -- Test.HaskellSupportSpec.tests - -- Test.View.CSSFrameworkSpec.tests - -- Test.View.FormSpec.tests - -- Test.Controller.ContextSpec.tests - -- Test.Controller.ParamSpec.tests - -- Test.Controller.AccessDeniedSpec.tests - -- Test.Controller.NotFoundSpec.tests - -- Test.SchemaMigrationSpec.tests - -- Test.ModelSupportSpec.tests - -- Test.SchemaCompilerSpec.tests - -- Test.QueryBuilderSpec.tests - -- Test.RouterSupportSpec.tests - -- Test.ViewSupportSpec.tests - -- Test.ServerSideComponent.HtmlParserSpec.tests - -- Test.ServerSideComponent.HtmlDiffSpec.tests + Test.IDE.SchemaDesigner.CompilerSpec.tests + Test.IDE.SchemaDesigner.ParserSpec.tests + Test.IDE.SchemaDesigner.Controller.EnumValuesSpec.tests + Test.IDE.SchemaDesigner.Controller.HelperSpec.tests + Test.IDE.SchemaDesigner.Controller.ValidationSpec.tests + Test.ValidationSupport.ValidateFieldSpec.tests + Test.IDE.CodeGeneration.ControllerGenerator.tests + Test.IDE.CodeGeneration.ViewGenerator.tests + Test.IDE.CodeGeneration.MailGenerator.tests + Test.IDE.CodeGeneration.JobGenerator.tests + Test.NameSupportSpec.tests + Test.HaskellSupportSpec.tests + Test.View.CSSFrameworkSpec.tests + Test.View.FormSpec.tests + Test.Controller.ContextSpec.tests + Test.Controller.ParamSpec.tests + Test.Controller.AccessDeniedSpec.tests + Test.Controller.NotFoundSpec.tests + Test.SchemaMigrationSpec.tests + Test.ModelSupportSpec.tests + Test.SchemaCompilerSpec.tests + Test.QueryBuilderSpec.tests + Test.RouterSupportSpec.tests + Test.ViewSupportSpec.tests + Test.ServerSideComponent.HtmlParserSpec.tests + Test.ServerSideComponent.HtmlDiffSpec.tests Test.FileStorage.ControllerFunctionsSpec.tests - -- Test.FileStorage.MimeTypesSpec.tests - -- Test.DataSync.DynamicQueryCompiler.tests - -- Test.IDE.SchemaDesigner.SchemaOperationsSpec.tests - -- Test.IDE.CodeGeneration.MigrationGenerator.tests - -- Test.Controller.CookieSpec.tests - -- Test.PGListenerSpec.tests - -- Test.SEO.Sitemap.tests + Test.FileStorage.MimeTypesSpec.tests + Test.DataSync.DynamicQueryCompiler.tests + Test.IDE.SchemaDesigner.SchemaOperationsSpec.tests + Test.IDE.CodeGeneration.MigrationGenerator.tests + Test.Controller.CookieSpec.tests + Test.PGListenerSpec.tests + Test.SEO.Sitemap.tests From 581b74865aae24b99772b0211020508b615273af Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 21:13:29 +0300 Subject: [PATCH 16/21] Remove line break --- Test/FileStorage/ControllerFunctionsSpec.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index e85a80678..599bbd6cd 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -28,7 +28,6 @@ tests = describe "IHP.FileStorage.ControllerFunctions" $ do let withFrameworkConfig = IHP.FrameworkConfig.withFrameworkConfig config - describe "storeFileWithOptions" $ do it "returns the objectPath without the baseUrl" $ do withSystemTempDirectory "ihp-test" $ \tempDir -> do From 9fc7bd9da04436162d1ce7d8d498931eb7b5ee4e Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 21:15:46 +0300 Subject: [PATCH 17/21] Import cleanups --- Test/FileStorage/ControllerFunctionsSpec.hs | 49 +++++++++------------ 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index 599bbd6cd..a14f0fede 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -5,18 +5,12 @@ import IHP.Prelude import IHP.FileStorage.ControllerFunctions import IHP.Controller.Context import IHP.FrameworkConfig -import IHP.ModelSupport -import IHP.FileStorage.Types -import System.IO.Temp (withSystemTempDirectory) -import qualified Data.ByteString.Lazy as LBS -import qualified Data.Text as Text -import Data.Default (def) -import Data.Time.Clock (getCurrentTime, addUTCTime) import Network.Wai as Wai (defaultRequest) import Network.Wai.Parse (FileInfo(..)) import IHP.Controller.RequestContext +import IHP.FileStorage.Types import IHP.FileStorage.Config -import qualified IHP.FrameworkConfig as Config + tests :: Spec @@ -30,26 +24,25 @@ tests = describe "IHP.FileStorage.ControllerFunctions" $ do describe "storeFileWithOptions" $ do it "returns the objectPath without the baseUrl" $ do - withSystemTempDirectory "ihp-test" $ \tempDir -> do - withFrameworkConfig \frameworkConfig -> do - context <- createControllerContext frameworkConfig - let ?context = context - - let fileInfo = FileInfo - { fileName = "test.txt" - , fileContentType = "text/plain" - , fileContent = "Hello, world!" - } - - -- We pass the UUID that will be used as the filename, so we can easily assert the objectPath. - let options :: StoreFileOptions = def - { fileName = Just "4c55dac2-e411-45ac-aa10-b957b01221df" - , directory = "Test.FileStorage.ControllerFunctionsSpec" - } - - result <- storeFileWithOptions fileInfo options - - result.url `shouldBe` ("Test.FileStorage.ControllerFunctionsSpec/4c55dac2-e411-45ac-aa10-b957b01221df") + withFrameworkConfig \frameworkConfig -> do + context <- createControllerContext frameworkConfig + let ?context = context + + let fileInfo = FileInfo + { fileName = "test.txt" + , fileContentType = "text/plain" + , fileContent = "Hello, world!" + } + + -- We pass the UUID that will be used as the filename, so we can easily assert the objectPath. + let options :: StoreFileOptions = def + { fileName = Just "4c55dac2-e411-45ac-aa10-b957b01221df" + , directory = "Test.FileStorage.ControllerFunctionsSpec" + } + + result <- storeFileWithOptions fileInfo options + + result.url `shouldBe` ("Test.FileStorage.ControllerFunctionsSpec/4c55dac2-e411-45ac-aa10-b957b01221df") describe "createTemporaryDownloadUrlFromPath" $ do it "returns baseUrl concatenated with objectPath when objectPath does not start with http:// or https://" $ do From d40ef016eb807a61ffe72daf24bd54af2755c3eb Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 21:16:04 +0300 Subject: [PATCH 18/21] More line breaks --- Test/FileStorage/ControllerFunctionsSpec.hs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index a14f0fede..e6d3c5bc1 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -70,7 +70,3 @@ createControllerContext frameworkConfig = do requestContext = RequestContext { request, respond = error "respond", requestBody, frameworkConfig = frameworkConfig } let ?requestContext = requestContext newControllerContext - - - - From ae9767b053c62e6852f8583b72bac1026982cd54 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 21:16:49 +0300 Subject: [PATCH 19/21] Line breaks --- Test/FileStorage/ControllerFunctionsSpec.hs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index e6d3c5bc1..9bb5e2d94 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -11,8 +11,6 @@ import IHP.Controller.RequestContext import IHP.FileStorage.Types import IHP.FileStorage.Config - - tests :: Spec tests = describe "IHP.FileStorage.ControllerFunctions" $ do From 4241679776c54444ed5eae9a9a911d7c90e7ac77 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Sun, 6 Oct 2024 21:17:38 +0300 Subject: [PATCH 20/21] Fix typo --- Test/FileStorage/ControllerFunctionsSpec.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index 9bb5e2d94..70768db4d 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -52,7 +52,7 @@ tests = describe "IHP.FileStorage.ControllerFunctions" $ do temporaryDownloadUrl.url `shouldBe` "http://localhost:8000/static/test.txt" - it "returns '/' concatenated with objectPath when objectPath starts with 'http://' or 'https://'" $ do + it "returns the objectPath when objectPath starts with 'http://' or 'https://'" $ do withFrameworkConfig \frameworkConfig -> do context <- createControllerContext frameworkConfig let ?context = context From adfaf3c28ae352df0e7cbd9ae3e8e65e7279dbd4 Mon Sep 17 00:00:00 2001 From: Amitai Burstein Date: Mon, 7 Oct 2024 09:29:44 +0300 Subject: [PATCH 21/21] Prefix objectPath with slash --- IHP/FileStorage/ControllerFunctions.hs | 7 ++++--- Test/FileStorage/ControllerFunctionsSpec.hs | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/IHP/FileStorage/ControllerFunctions.hs b/IHP/FileStorage/ControllerFunctions.hs index 9ae9dab88..cd9901ea8 100644 --- a/IHP/FileStorage/ControllerFunctions.hs +++ b/IHP/FileStorage/ControllerFunctions.hs @@ -112,7 +112,8 @@ storeFileWithOptions fileInfo options = do |> LBS.writeFile (cs destPath) let frameworkConfig = ?context.frameworkConfig - pure $ objectPath + -- Prefix with a slash so it can be used in URLs, even if the baseUrl is empty. + pure $ "/" <> objectPath S3Storage { connectInfo, bucket, baseUrl } -> do let payload = fileInfo |> (.fileContent) @@ -231,8 +232,8 @@ createTemporaryDownloadUrlFromPathWithExpiredAt validInSeconds objectPath = do let url = if any (`isPrefixOf` objectPath) urlSchemes -- BC, before we saved only the relative path of a file, we saved the full URL. So use it as is. then objectPath - -- We have the relative path, so add the baseUrl. - else frameworkConfig.baseUrl <> "/" <> objectPath + -- We have the relative path (prefixed with slash), so add the baseUrl. + else frameworkConfig.baseUrl <> objectPath pure TemporaryDownloadUrl { url = cs url, expiredAt = publicUrlExpiredAt } S3Storage { connectInfo, bucket} -> do diff --git a/Test/FileStorage/ControllerFunctionsSpec.hs b/Test/FileStorage/ControllerFunctionsSpec.hs index 70768db4d..02f6892a0 100644 --- a/Test/FileStorage/ControllerFunctionsSpec.hs +++ b/Test/FileStorage/ControllerFunctionsSpec.hs @@ -40,14 +40,14 @@ tests = describe "IHP.FileStorage.ControllerFunctions" $ do result <- storeFileWithOptions fileInfo options - result.url `shouldBe` ("Test.FileStorage.ControllerFunctionsSpec/4c55dac2-e411-45ac-aa10-b957b01221df") + result.url `shouldBe` ("/Test.FileStorage.ControllerFunctionsSpec/4c55dac2-e411-45ac-aa10-b957b01221df") describe "createTemporaryDownloadUrlFromPath" $ do it "returns baseUrl concatenated with objectPath when objectPath does not start with http:// or https://" $ do withFrameworkConfig \frameworkConfig -> do context <- createControllerContext frameworkConfig let ?context = context - let objectPath = "static/test.txt" + let objectPath = "/static/test.txt" temporaryDownloadUrl <- createTemporaryDownloadUrlFromPath objectPath temporaryDownloadUrl.url `shouldBe` "http://localhost:8000/static/test.txt"