diff --git a/src/MyWebLog.Data/RethinkDbData.fs b/src/MyWebLog.Data/RethinkDbData.fs index 295f830..1c035e3 100644 --- a/src/MyWebLog.Data/RethinkDbData.fs +++ b/src/MyWebLog.Data/RethinkDbData.fs @@ -184,6 +184,9 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger()) + /// Delete assets for the given theme ID let deleteAssetsByTheme themeId = rethink { withTable Table.ThemeAsset @@ -876,7 +879,7 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger { withTable Table.ThemeAsset - without [ nameof ThemeAsset.Empty.Data ] + merge (r.HashMap(nameof ThemeAsset.Empty.Data, emptyFile)) result; withRetryDefault conn } @@ -891,7 +894,7 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger { withTable Table.ThemeAsset filter (matchAssetByThemeId themeId) - without [ nameof ThemeAsset.Empty.Data ] + merge (r.HashMap(nameof ThemeAsset.Empty.Data, emptyFile)) result; withRetryDefault conn } @@ -950,7 +953,7 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger obj; r.Minval() |] [| webLogId :> obj; r.Maxval() |] [ Index Index.WebLogAndPath ] - without [ nameof Upload.Empty.Data ] + merge (r.HashMap(nameof Upload.Empty.Data, emptyFile)) resultCursor; withRetryCursorDefault; toList conn } diff --git a/src/MyWebLog.Tests/Data/PostgresDataTests.fs b/src/MyWebLog.Tests/Data/PostgresDataTests.fs index 6297a8c..6e1c6ab 100644 --- a/src/MyWebLog.Tests/Data/PostgresDataTests.fs +++ b/src/MyWebLog.Tests/Data/PostgresDataTests.fs @@ -462,6 +462,52 @@ let themeTests = testList "Theme" [ ] ] +let themeAssetTests = testList "ThemeAsset" [ + testList "Save" [ + testTask "succeeds when adding an asset" { + do! ThemeDataTests.Asset.``Save succeeds when adding an asset`` (mkData ()) + } + testTask "succeeds when updating an asset" { + do! ThemeDataTests.Asset.``Save succeeds when updating an asset`` (mkData ()) + } + ] + testTask "All succeeds" { + do! ThemeDataTests.Asset.``All succeeds`` (mkData ()) + } + testList "FindById" [ + testTask "succeeds when an asset is found" { + do! ThemeDataTests.Asset.``FindById succeeds when an asset is found`` (mkData ()) + } + testTask "succeeds when an asset is not found" { + do! ThemeDataTests.Asset.``FindById succeeds when an asset is not found`` (mkData ()) + } + ] + testList "FindByTheme" [ + testTask "succeeds when assets exist" { + do! ThemeDataTests.Asset.``FindByTheme succeeds when assets exist`` (mkData ()) + } + testTask "succeeds when assets do not exist" { + do! ThemeDataTests.Asset.``FindByTheme succeeds when assets do not exist`` (mkData ()) + } + ] + testList "FindByThemeWithData" [ + testTask "succeeds when assets exist" { + do! ThemeDataTests.Asset.``FindByThemeWithData succeeds when assets exist`` (mkData ()) + } + testTask "succeeds when assets do not exist" { + do! ThemeDataTests.Asset.``FindByThemeWithData succeeds when assets do not exist`` (mkData ()) + } + ] + testList "DeleteByTheme" [ + testTask "succeeds when assets are deleted" { + do! ThemeDataTests.Asset.``DeleteByTheme succeeds when assets are deleted`` (mkData ()) + } + testTask "succeeds when no assets are deleted" { + do! ThemeDataTests.Asset.``DeleteByTheme succeeds when no assets are deleted`` (mkData ()) + } + ] +] + /// Drop the throwaway PostgreSQL database let environmentCleanUp = test "Clean Up" { if db.IsSome then db.Value.Dispose() @@ -476,5 +522,6 @@ let all = postTests tagMapTests themeTests + themeAssetTests environmentCleanUp ] |> testSequenced diff --git a/src/MyWebLog.Tests/Data/RethinkDbDataTests.fs b/src/MyWebLog.Tests/Data/RethinkDbDataTests.fs index e4d7001..867d542 100644 --- a/src/MyWebLog.Tests/Data/RethinkDbDataTests.fs +++ b/src/MyWebLog.Tests/Data/RethinkDbDataTests.fs @@ -462,6 +462,52 @@ let themeTests = testList "Theme" [ ] ] +let themeAssetTests = testList "ThemeAsset" [ + testList "Save" [ + testTask "succeeds when adding an asset" { + do! ThemeDataTests.Asset.``Save succeeds when adding an asset`` data.Value + } + testTask "succeeds when updating an asset" { + do! ThemeDataTests.Asset.``Save succeeds when updating an asset`` data.Value + } + ] + testTask "All succeeds" { + do! ThemeDataTests.Asset.``All succeeds`` data.Value + } + testList "FindById" [ + testTask "succeeds when an asset is found" { + do! ThemeDataTests.Asset.``FindById succeeds when an asset is found`` data.Value + } + testTask "succeeds when an asset is not found" { + do! ThemeDataTests.Asset.``FindById succeeds when an asset is not found`` data.Value + } + ] + testList "FindByTheme" [ + testTask "succeeds when assets exist" { + do! ThemeDataTests.Asset.``FindByTheme succeeds when assets exist`` data.Value + } + testTask "succeeds when assets do not exist" { + do! ThemeDataTests.Asset.``FindByTheme succeeds when assets do not exist`` data.Value + } + ] + testList "FindByThemeWithData" [ + testTask "succeeds when assets exist" { + do! ThemeDataTests.Asset.``FindByThemeWithData succeeds when assets exist`` data.Value + } + testTask "succeeds when assets do not exist" { + do! ThemeDataTests.Asset.``FindByThemeWithData succeeds when assets do not exist`` data.Value + } + ] + testList "DeleteByTheme" [ + testTask "succeeds when assets are deleted" { + do! ThemeDataTests.Asset.``DeleteByTheme succeeds when assets are deleted`` data.Value + } + testTask "succeeds when no assets are deleted" { + do! ThemeDataTests.Asset.``DeleteByTheme succeeds when no assets are deleted`` data.Value + } + ] +] + /// Drop the throwaway RethinkDB database let environmentCleanUp = testTask "Clean Up" { do! disposeData () @@ -476,5 +522,6 @@ let all = postTests tagMapTests themeTests + themeAssetTests environmentCleanUp ] |> testSequenced diff --git a/src/MyWebLog.Tests/Data/SQLiteDataTests.fs b/src/MyWebLog.Tests/Data/SQLiteDataTests.fs index 2410c91..7e7dba7 100644 --- a/src/MyWebLog.Tests/Data/SQLiteDataTests.fs +++ b/src/MyWebLog.Tests/Data/SQLiteDataTests.fs @@ -683,6 +683,74 @@ let themeTests = testList "Theme" [ ] ] +let themeAssetTests = testList "ThemeAsset" [ + testList "Save" [ + testTask "succeeds when adding an asset" { + let data = mkData () + try do! ThemeDataTests.Asset.``Save succeeds when adding an asset`` data + finally dispose data + } + testTask "succeeds when updating an asset" { + let data = mkData () + try do! ThemeDataTests.Asset.``Save succeeds when updating an asset`` data + finally dispose data + } + ] + testTask "All succeeds" { + let data = mkData () + try do! ThemeDataTests.Asset.``All succeeds`` data + finally dispose data + } + testList "FindById" [ + testTask "succeeds when an asset is found" { + let data = mkData () + try do! ThemeDataTests.Asset.``FindById succeeds when an asset is found`` data + finally dispose data + } + testTask "succeeds when an asset is not found" { + let data = mkData () + try do! ThemeDataTests.Asset.``FindById succeeds when an asset is not found`` data + finally dispose data + } + ] + testList "FindByTheme" [ + testTask "succeeds when assets exist" { + let data = mkData () + try do! ThemeDataTests.Asset.``FindByTheme succeeds when assets exist`` data + finally dispose data + } + testTask "succeeds when assets do not exist" { + let data = mkData () + try do! ThemeDataTests.Asset.``FindByTheme succeeds when assets do not exist`` data + finally dispose data + } + ] + testList "FindByThemeWithData" [ + testTask "succeeds when assets exist" { + let data = mkData () + try do! ThemeDataTests.Asset.``FindByThemeWithData succeeds when assets exist`` data + finally dispose data + } + testTask "succeeds when assets do not exist" { + let data = mkData () + try do! ThemeDataTests.Asset.``FindByThemeWithData succeeds when assets do not exist`` data + finally dispose data + } + ] + testList "DeleteByTheme" [ + testTask "succeeds when assets are deleted" { + let data = mkData () + try do! ThemeDataTests.Asset.``DeleteByTheme succeeds when assets are deleted`` data + finally dispose data + } + testTask "succeeds when no assets are deleted" { + let data = mkData () + try do! ThemeDataTests.Asset.``DeleteByTheme succeeds when no assets are deleted`` data + finally dispose data + } + ] +] + /// Delete the SQLite database let environmentCleanUp = test "Clean Up" { File.Delete dbName @@ -698,5 +766,6 @@ let all = postTests tagMapTests themeTests + themeAssetTests environmentCleanUp ] |> testSequenced diff --git a/src/MyWebLog.Tests/Data/ThemeDataTests.fs b/src/MyWebLog.Tests/Data/ThemeDataTests.fs index dd4228c..5233187 100644 --- a/src/MyWebLog.Tests/Data/ThemeDataTests.fs +++ b/src/MyWebLog.Tests/Data/ThemeDataTests.fs @@ -3,13 +3,24 @@ /// module ThemeDataTests +open System.IO open Expecto open MyWebLog open MyWebLog.Data +open NodaTime /// The ID of the default theme (restored from root-weblog.json) let private defaultId = ThemeId "default" +/// The ID of the test theme loaded and manipulated by these tests +let private testId = ThemeId "test-theme" + +/// The dark version of the myWebLog logo +let private darkFile = File.ReadAllBytes "../admin-theme/wwwroot/logo-dark.png" + +/// The light version of the myWebLog logo +let private lightFile = File.ReadAllBytes "../admin-theme/wwwroot/logo-light.png" + /// Ensure that theme templates do not have any text let private ensureNoText theme = for template in theme.Templates do @@ -64,18 +75,17 @@ let ``FindByIdWithoutText succeeds when the theme does not exist`` (data: IData) } let ``Save succeeds when adding a theme`` (data: IData) = task { - let themeId = ThemeId "test-theme" do! data.Theme.Save - { Id = themeId + { Id = testId Name = "Test Theme" Version = "evergreen" Templates = [ { Name = "index"; Text = "

{{ values_here }}

" } { Name = "single-post"; Text = "

{{ the_post }}" } ] } - let! saved = data.Theme.FindById themeId + let! saved = data.Theme.FindById testId Expect.isSome saved "There should have been a theme returned" let it = saved.Value - Expect.equal it.Id themeId "ID was incorrect" + Expect.equal it.Id testId "ID was incorrect" Expect.equal it.Name "Test Theme" "Name was incorrect" Expect.equal it.Version "evergreen" "Version was incorrect" Expect.hasLength it.Templates 2 "There should have been 2 templates" @@ -86,19 +96,18 @@ let ``Save succeeds when adding a theme`` (data: IData) = task { } let ``Save succeeds when updating a theme`` (data: IData) = task { - let themeId = ThemeId "test-theme" do! data.Theme.Save - { Id = themeId + { Id = testId Name = "Updated Theme" Version = "still evergreen" Templates = [ { Name = "index"; Text = "

{{ values_there }}

" } { Name = "layout"; Text = "" } { Name = "single-post"; Text = "

{{ the_post }}" } ] } - let! updated = data.Theme.FindById themeId + let! updated = data.Theme.FindById testId Expect.isSome updated "The updated theme should have been returned" let it = updated.Value - Expect.equal it.Id themeId "ID was incorrect" + Expect.equal it.Id testId "ID was incorrect" Expect.equal it.Name "Updated Theme" "Name was incorrect" Expect.equal it.Version "still evergreen" "Version was incorrect" Expect.hasLength it.Templates 3 "There should have been 3 templates" @@ -111,11 +120,115 @@ let ``Save succeeds when updating a theme`` (data: IData) = task { } let ``Delete succeeds when a theme is deleted`` (data: IData) = task { - let! deleted = data.Theme.Delete (ThemeId "test-theme") + // Delete should also delete assets associated with the theme + do! data.ThemeAsset.Save { Id = ThemeAssetId (testId, "logo-dark.png"); UpdatedOn = Noda.epoch; Data = darkFile } + do! data.ThemeAsset.Save { Id = ThemeAssetId (testId, "logo-light.png"); UpdatedOn = Noda.epoch; Data = lightFile } + let! deleted = data.Theme.Delete testId Expect.isTrue deleted "The theme should have been deleted" + let! assets = data.ThemeAsset.FindByTheme testId + Expect.isEmpty assets "The theme's assets should have been deleted" } let ``Delete succeeds when a theme is not deleted`` (data: IData) = task { let! deleted = data.Theme.Delete (ThemeId "test-theme") // already deleted above Expect.isFalse deleted "The theme should not have been deleted" } + +///

+/// Integration tests for implementations +/// +module Asset = + + /// The theme ID for which assets will be tested + let private assetThemeId = ThemeId "asset-test" + + /// The asset ID for the dark logo + let private darkId = ThemeAssetId (assetThemeId, "logo-dark.png") + + /// The asset ID for the light logo + let private lightId = ThemeAssetId (assetThemeId, "logo-light.png") + + let ``Save succeeds when adding an asset`` (data: IData) = task { + do! data.Theme.Save { Theme.Empty with Id = assetThemeId } + do! data.ThemeAsset.Save { Id = lightId; UpdatedOn = Noda.epoch + Duration.FromDays 18; Data = lightFile } + let! asset = data.ThemeAsset.FindById lightId + Expect.isSome asset "The asset should have been found" + let it = asset.Value + Expect.equal it.Id lightId "ID was incorrect" + Expect.equal it.UpdatedOn (Noda.epoch + Duration.FromDays 18) "Updated on was incorrect" + Expect.equal it.Data lightFile "Data was incorrect" + } + + let ``Save succeeds when updating an asset`` (data: IData) = task { + do! data.ThemeAsset.Save { Id = lightId; UpdatedOn = Noda.epoch + Duration.FromDays 20; Data = darkFile } + let! asset = data.ThemeAsset.FindById lightId + Expect.isSome asset "The asset should have been found" + let it = asset.Value + Expect.equal it.Id lightId "ID was incorrect" + Expect.equal it.UpdatedOn (Noda.epoch + Duration.FromDays 20) "Updated on was incorrect" + Expect.equal it.Data darkFile "Data was incorrect" + } + + let ``All succeeds`` (data: IData) = task { + let! all = data.ThemeAsset.All() + Expect.hasLength all 2 "There should have been 2 assets retrieved" + for asset in all do + Expect.contains + [ ThemeAssetId (defaultId, "style.css"); lightId ] asset.Id $"Unexpected asset found ({asset.Id})" + Expect.isEmpty asset.Data $"Asset {asset.Id} should not have had data" + } + + let ``FindById succeeds when an asset is found`` (data: IData) = task { + let! asset = data.ThemeAsset.FindById lightId + Expect.isSome asset "The asset should have been found" + let it = asset.Value + Expect.equal it.Id lightId "ID was incorrect" + Expect.equal it.UpdatedOn (Noda.epoch + Duration.FromDays 20) "Updated on was incorrect" + Expect.equal it.Data darkFile "Data was incorrect" + } + + let ``FindById succeeds when an asset is not found`` (data: IData) = task { + let! asset = data.ThemeAsset.FindById (ThemeAssetId (assetThemeId, "404.jpg")) + Expect.isNone asset "There should not have been an asset returned" + } + + let ``FindByTheme succeeds when assets exist`` (data: IData) = task { + do! data.ThemeAsset.Save { Id = darkId; UpdatedOn = Noda.epoch; Data = darkFile } + do! data.ThemeAsset.Save { Id = lightId; UpdatedOn = Noda.epoch; Data = lightFile } + let! assets = data.ThemeAsset.FindByTheme assetThemeId + Expect.hasLength assets 2 "There should have been 2 assets returned" + for asset in assets do + Expect.contains [ darkId; lightId ] asset.Id $"Unexpected asset found ({asset.Id})" + Expect.equal asset.UpdatedOn Noda.epoch $"Updated on was incorrect ({asset.Id})" + Expect.isEmpty asset.Data $"Data should not have been retrieved ({asset.Id})" + } + + let ``FindByTheme succeeds when assets do not exist`` (data: IData) = task { + let! assets = data.ThemeAsset.FindByTheme (ThemeId "no-assets-here") + Expect.isEmpty assets "There should have been no assets returned" + } + + let ``FindByThemeWithData succeeds when assets exist`` (data: IData) = task { + let! assets = data.ThemeAsset.FindByThemeWithData assetThemeId + Expect.hasLength assets 2 "There should have been 2 assets returned" + let darkLogo = assets |> List.find (fun it -> it.Id = darkId) + Expect.equal darkLogo.Data darkFile "The dark asset's data is incorrect" + let lightLogo = assets |> List.find (fun it -> it.Id = lightId) + Expect.equal lightLogo.Data lightFile "The light asset's data is incorrect" + } + + let ``FindByThemeWithData succeeds when assets do not exist`` (data: IData) = task { + let! assets = data.ThemeAsset.FindByThemeWithData (ThemeId "still-no-assets") + Expect.isEmpty assets "There should have been no assets returned" + } + + let ``DeleteByTheme succeeds when assets are deleted`` (data: IData) = task { + do! data.ThemeAsset.DeleteByTheme assetThemeId + let! assets = data.ThemeAsset.FindByTheme assetThemeId + Expect.isEmpty assets "There should be no assets remaining" + } + + let ``DeleteByTheme succeeds when no assets are deleted`` (data: IData) = task { + do! data.ThemeAsset.DeleteByTheme assetThemeId // already deleted above + Expect.isTrue true "The above did not raise an exception; that's the test" + }