diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs index 1a80e62f0..94b191d58 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguageConfig.cs @@ -58,5 +58,17 @@ namespace Squidex.Domain.Apps.Core.Apps this.language = language; this.languageFallbacks = fallback ?? Array.Empty(); } + + public LanguageConfig WithoutFallback(Language fallback) + { + Guard.NotNull(fallback); + + if (languageFallbacks.Contains(fallback)) + { + return new LanguageConfig(Language, IsOptional, LanguageFallbacks.Except(new[] { fallback })); + } + + return this; + } } } diff --git a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs index 88cb22428..0a65412c2 100644 --- a/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs +++ b/backend/src/Squidex.Domain.Apps.Core.Model/Apps/LanguagesConfig.cs @@ -89,19 +89,26 @@ namespace Squidex.Domain.Apps.Core.Apps } [Pure] - public LanguagesConfig MakeMaster(Language language) + public LanguagesConfig Set(Language language, bool isOptional = false, IEnumerable? fallback = null) { Guard.NotNull(language); - return Create(languages, languages[language]); + return Set(new LanguageConfig(language, isOptional, fallback)); } [Pure] - public LanguagesConfig Set(Language language, bool isOptional = false, IEnumerable? fallback = null) + public LanguagesConfig MakeMaster(Language language) { Guard.NotNull(language); - return Set(new LanguageConfig(language, isOptional, fallback)); + var newMaster = new LanguageConfig(languages[language].Language); + + var newLanguages = + languages + .Without(language) + .With(language, newMaster); + + return Create(newLanguages, newMaster); } [Pure] @@ -111,7 +118,10 @@ namespace Squidex.Domain.Apps.Core.Apps var newLanguages = languages.With(config.Language, config); - var newMaster = Master?.Language == config.Language ? config : Master; + var newMaster = + Master.Language != config.Language ? + Master : + config; return Create(newLanguages, newMaster!); } @@ -122,12 +132,7 @@ namespace Squidex.Domain.Apps.Core.Apps Guard.NotNull(language); var newLanguages = - languages.Values.Where(x => x.Language != language) - .Select(config => new LanguageConfig( - config.Language, - config.IsOptional, - config.LanguageFallbacks.Except(new[] { language }))) - .ToArrayDictionary(x => x.Language); + languages.Values.Where(x => x.Language != language).Select(x => x.WithoutFallback(language)).ToArrayDictionary(x => x.Language); var newMaster = newLanguages.Values.FirstOrDefault(x => x.Language == Master.Language) ?? diff --git a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs index b66614b54..abab0038c 100644 --- a/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs +++ b/backend/tests/Squidex.Domain.Apps.Core.Tests/Model/Apps/LanguagesConfigTests.cs @@ -14,15 +14,17 @@ using Squidex.Domain.Apps.Core.Apps; using Squidex.Infrastructure; using Xunit; +#pragma warning disable SA1310 // Field names must not contain underscore + namespace Squidex.Domain.Apps.Core.Model.Apps { public class LanguagesConfigTests { + private readonly LanguagesConfig config_0 = LanguagesConfig.Build(Language.DE); + [Fact] public void Should_create_initial_config_0() { - var config_0 = LanguagesConfig.Build(Language.DE); - config_0.OfType().ToList().Should().BeEquivalentTo( new List { @@ -37,9 +39,9 @@ namespace Squidex.Domain.Apps.Core.Model.Apps [Fact] public void Should_create_initial_config_0_with_multiple_languages() { - var config_0 = LanguagesConfig.Build(Language.DE, Language.EN, Language.IT); + var config = LanguagesConfig.Build(Language.DE, Language.EN, Language.IT); - config_0.OfType().ToList().Should().BeEquivalentTo( + config.OfType().ToList().Should().BeEquivalentTo( new List { new LanguageConfig(Language.DE), @@ -47,12 +49,12 @@ namespace Squidex.Domain.Apps.Core.Model.Apps new LanguageConfig(Language.IT) }); - config_0.TryGetConfig(Language.DE, out var master); + config.TryGetConfig(Language.DE, out var master); - Assert.Equal(Language.DE, config_0.Master.Language); - Assert.Equal(3, config_0.Count); + Assert.Equal(Language.DE, config.Master.Language); + Assert.Equal(3, config.Count); - Assert.Same(master, config_0.Master); + Assert.Same(master, config.Master); } [Fact] @@ -64,18 +66,17 @@ namespace Squidex.Domain.Apps.Core.Model.Apps new LanguageConfig(Language.EN), new LanguageConfig(Language.IT) }; - var config_0 = LanguagesConfig.Build(configs); + var config = LanguagesConfig.Build(configs); - config_0.OfType().ToList().Should().BeEquivalentTo(configs); + config.OfType().ToList().Should().BeEquivalentTo(configs); - Assert.Equal(configs[0], config_0.Master); - Assert.Same(configs[0], config_0.Master); + Assert.Equal(configs[0], config.Master); + Assert.Same(configs[0], config.Master); } [Fact] public void Should_add_language() { - var config_0 = LanguagesConfig.Build(Language.DE); var config_1 = config_0.Set(Language.IT); config_1.OfType().ToList().Should().BeEquivalentTo( @@ -92,36 +93,30 @@ namespace Squidex.Domain.Apps.Core.Model.Apps [Fact] public void Should_make_first_language_to_master() { - var config_0 = LanguagesConfig.Build(Language.IT); - - Assert.Equal(Language.IT, config_0.Master.Language); + Assert.Equal(Language.DE, config_0.Master.Language); } [Fact] public void Should_not_throw_exception_if_language_to_add_already_exists() { - var config_0 = LanguagesConfig.Build(Language.DE); - config_0.Set(Language.DE); } [Fact] public void Should_make_master_language() { - var config_0 = LanguagesConfig.Build(Language.DE); - var config_1 = config_0.Set(Language.UK); - var config_2 = config_1.Set(Language.IT); + var config_2 = config_1.Set(Language.IT, true, new[] { Language.UK }); var config_3 = config_2.MakeMaster(Language.IT); Assert.Equal(Language.IT, config_3.Master.Language); + + Assert.False(config_3.Master.IsOptional); } [Fact] public void Should_return_same_languages_if_master_language_is_already_master() { - var config_0 = LanguagesConfig.Build(Language.DE); - var config_1 = config_0.Set(Language.UK); var config_2 = config_1.Set(Language.IT); var config_3 = config_2.MakeMaster(Language.IT); @@ -133,24 +128,20 @@ namespace Squidex.Domain.Apps.Core.Model.Apps [Fact] public void Should_throw_exception_if_language_to_make_master_is_not_found() { - var config_0 = LanguagesConfig.Build(Language.DE); - Assert.Throws(() => config_0.MakeMaster(Language.EN)); } [Fact] public void Should_not_throw_exception_if_language_is_already_master_language() { - var config_0 = LanguagesConfig.Build(Language.DE); - config_0.MakeMaster(Language.DE); } [Fact] public void Should_remove_language() { - var config_0 = LanguagesConfig.Build(Language.DE, Language.IT, Language.RU); - var config_1 = config_0.Remove(Language.IT); + var config = LanguagesConfig.Build(Language.DE, Language.IT, Language.RU); + var config_1 = config.Remove(Language.IT); Assert.Same(config_1.Master, config_1.OfType().FirstOrDefault(x => x.Language == Language.DE)); @@ -165,12 +156,12 @@ namespace Squidex.Domain.Apps.Core.Model.Apps [Fact] public void Should_remove_fallbacks_when_removing_language() { - var config_0 = + var config = LanguagesConfig.Build( new LanguageConfig(Language.DE), new LanguageConfig(Language.IT, false, Language.RU, Language.IT), new LanguageConfig(Language.RU, false, Language.DE, Language.IT)); - var config_1 = config_0.Remove(Language.IT); + var config_1 = config.Remove(Language.IT); config_1.OfType().ToList().Should().BeEquivalentTo( new List @@ -181,9 +172,8 @@ namespace Squidex.Domain.Apps.Core.Model.Apps } [Fact] - public void Should_do_nothing_if_language_to_remove_is_not_found() + public void Should_return_same_languages_if_language_to_remove_is_not_found() { - var config_0 = LanguagesConfig.Build(Language.DE); var config_1 = config_0.Remove(Language.EN); Assert.Equal(config_0, config_1); @@ -192,15 +182,12 @@ namespace Squidex.Domain.Apps.Core.Model.Apps [Fact] public void Should_throw_exception_if_language_to_remove_is_master() { - var config_0 = LanguagesConfig.Build(Language.DE); - Assert.Throws(() => config_0.Remove(Language.DE)); } [Fact] public void Should_update_language() { - var config_0 = LanguagesConfig.Build(Language.DE); var config_1 = config_0.Set(new LanguageConfig(Language.IT)); var config_2 = config_1.Set(new LanguageConfig(Language.IT, true, Language.DE)); @@ -215,16 +202,12 @@ namespace Squidex.Domain.Apps.Core.Model.Apps [Fact] public void Should_throw_exception_if_fallback_language_is_invalid() { - var config_0 = LanguagesConfig.Build(Language.DE); - Assert.Throws(() => config_0.Set(new LanguageConfig(Language.DE, false, Language.EN))); } [Fact] public void Should_provide_enumerators() { - var config_0 = LanguagesConfig.Build(Language.DE); - Assert.NotEmpty(config_0); Assert.NotNull(((IEnumerable)config_0).GetEnumerator()); @@ -234,8 +217,6 @@ namespace Squidex.Domain.Apps.Core.Model.Apps [Fact] public void Should_resolve_language_by_string() { - var config_0 = LanguagesConfig.Build(Language.DE); - Assert.True(config_0.TryGetItem("de", out var item)); Assert.NotNull(item); } @@ -243,8 +224,6 @@ namespace Squidex.Domain.Apps.Core.Model.Apps [Fact] public void Should_not_resolve_language_fór_invalid_language() { - var config_0 = LanguagesConfig.Build(Language.DE); - Assert.False(config_0.TryGetItem("invalid", out var item)); Assert.Null(item); } diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/AppCreationTests.cs b/backend/tools/TestSuite/TestSuite.ApiTests/AppCreationTests.cs index 577127095..b0e87084d 100644 --- a/backend/tools/TestSuite/TestSuite.ApiTests/AppCreationTests.cs +++ b/backend/tools/TestSuite/TestSuite.ApiTests/AppCreationTests.cs @@ -30,6 +30,7 @@ namespace TestSuite.ApiTests { var appName = Guid.NewGuid().ToString(); + // STEP 1: Create app var createRequest = new CreateAppDto { Name = appName }; var app = await _.Apps.PostAppAsync(createRequest); @@ -37,16 +38,22 @@ namespace TestSuite.ApiTests // Should return create app with correct name. Assert.Equal(appName, app.Name); + + // STEP 2: Get all apps var apps = await _.Apps.GetAppsAsync(); // Should provide new app when apps are queried. Assert.Contains(apps, x => x.Name == appName); + + // STEP 3: Check contributors var contributors = await _.Apps.GetContributorsAsync(appName); // Should not client itself as a contributor. Assert.Empty(contributors.Items); + + // STEP 4: Check clients var clients = await _.Apps.GetClientsAsync(appName); // Should create default client. @@ -58,7 +65,13 @@ namespace TestSuite.ApiTests { var appName = Guid.NewGuid().ToString(); - await _.Apps.PostAppAsync(new CreateAppDto { Name = appName }); + // STEP 1: Create app + var createRequest = new CreateAppDto { Name = appName }; + + await _.Apps.PostAppAsync(createRequest); + + + // STEP 2: Archive app await _.Apps.DeleteAppAsync(appName); var apps = await _.Apps.GetAppsAsync(); diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/AppLanguagesTests.cs b/backend/tools/TestSuite/TestSuite.ApiTests/AppLanguagesTests.cs new file mode 100644 index 000000000..47cc4ecee --- /dev/null +++ b/backend/tools/TestSuite/TestSuite.ApiTests/AppLanguagesTests.cs @@ -0,0 +1,114 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Squidex.ClientLibrary.Management; +using TestSuite.Fixtures; +using Xunit; + +#pragma warning disable SA1300 // Element should begin with upper-case letter +#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row + +namespace TestSuite.ApiTests +{ + public sealed class AppLanguagesTests : IClassFixture + { + public ClientFixture _ { get; } + + public AppLanguagesTests(ClientFixture fixture) + { + _ = fixture; + } + + [Fact] + public async Task Should_manage_languages() + { + var appName = Guid.NewGuid().ToString(); + + // STEP 1: Add app + var createRequest = new CreateAppDto { Name = appName }; + + await _.Apps.PostAppAsync(createRequest); + + + // STEP 2: Add languages + await _.Apps.PostLanguageAsync(appName, new AddLanguageDto { Language = "de" }); + await _.Apps.PostLanguageAsync(appName, new AddLanguageDto { Language = "it" }); + await _.Apps.PostLanguageAsync(appName, new AddLanguageDto { Language = "fr" }); + + var languages = await _.Apps.GetLanguagesAsync(appName); + + var languageEN = languages.Items.First(x => x.Iso2Code == "en"); + + Assert.Equal(new string[] { "en", "de", "fr", "it" }, languages.Items.Select(x => x.Iso2Code).ToArray()); + Assert.True(languageEN.IsMaster); + + + // STEP 3: Update language1 + var updateRequest1 = new UpdateLanguageDto + { + Fallback = new string[] + { + "fr", + "it" + }, + IsOptional = true + }; + + languages = await _.Apps.PutLanguageAsync(appName, "de", updateRequest1); + + var languageDE = languages.Items.First(x => x.Iso2Code == "de"); + + Assert.Equal(new string[] { "fr", "it" }, languageDE.Fallback.ToArray()); + Assert.True(languageDE.IsOptional); + + + // STEP 4: Update language2 + var updateRequest2 = new UpdateLanguageDto + { + Fallback = new string[] + { + "fr", + "it" + } + }; + + languages = await _.Apps.PutLanguageAsync(appName, "en", updateRequest2); + + languageEN = languages.Items.First(x => x.Iso2Code == "en"); + + Assert.Equal(new string[] { "fr", "it" }, updateRequest2.Fallback.ToArray()); + + + // STEP 5: Change master language + var masterRequest = new UpdateLanguageDto { IsMaster = true }; + + languages = await _.Apps.PutLanguageAsync(appName, "de", masterRequest); + + languageDE = languages.Items.First(x => x.Iso2Code == "de"); + languageEN = languages.Items.First(x => x.Iso2Code == "en"); + + Assert.True(languageDE.IsMaster); + Assert.False(languageDE.IsOptional); + Assert.False(languageEN.IsMaster); + Assert.Empty(languageDE.Fallback); + Assert.Equal(new string[] { "de", "en", "fr", "it" }, languages.Items.Select(x => x.Iso2Code).ToArray()); + + + // STEP 6: RemoveRequest + languages = await _.Apps.DeleteLanguageAsync(appName, "fr"); + + languageEN = languages.Items.First(x => x.Iso2Code == "en"); + + Assert.Equal(new string[] { "it" }, languageEN.Fallback.ToArray()); + Assert.Equal(new string[] { "de", "en", "it" }, languages.Items.Select(x => x.Iso2Code).ToArray()); + } + } +} diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/AppsTests.cs b/backend/tools/TestSuite/TestSuite.ApiTests/AppTests.cs similarity index 95% rename from backend/tools/TestSuite/TestSuite.ApiTests/AppsTests.cs rename to backend/tools/TestSuite/TestSuite.ApiTests/AppTests.cs index 731ca2ae6..560a06ee8 100644 --- a/backend/tools/TestSuite/TestSuite.ApiTests/AppsTests.cs +++ b/backend/tools/TestSuite/TestSuite.ApiTests/AppTests.cs @@ -18,15 +18,23 @@ using Xunit; namespace TestSuite.ApiTests { - public sealed class AppsTests : IClassFixture + public sealed class AppTests : IClassFixture { public CreatedAppFixture _ { get; } - public AppsTests(CreatedAppFixture fixture) + public AppTests(CreatedAppFixture fixture) { _ = fixture; } + [Fact] + public async Task Should_get_app() + { + var app = await _.Apps.GetAppAsync(_.AppName); + + Assert.Equal(_.AppName, app.Name); + } + [Fact] public async Task Should_manage_clients() { diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/AssetFormatTests.cs b/backend/tools/TestSuite/TestSuite.ApiTests/AssetFormatTests.cs new file mode 100644 index 000000000..c11b46f0e --- /dev/null +++ b/backend/tools/TestSuite/TestSuite.ApiTests/AssetFormatTests.cs @@ -0,0 +1,142 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using System.Threading.Tasks; +using Squidex.ClientLibrary.Management; +using TestSuite.Fixtures; +using Xunit; + +#pragma warning disable SA1300 // Element should begin with upper-case letter +#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row + +namespace TestSuite.ApiTests +{ + public class AssetFormatTests : IClassFixture + { + public AssetFixture _ { get; } + + public AssetFormatTests(AssetFixture fixture) + { + _ = fixture; + } + + [Fact] + public async Task Should_upload_image_gif() + { + var asset = await _.UploadFileAsync("Assets/SampleGIFImage_40kbmb.gif", "image/gif"); + + // Should create metadata. + Assert.True(asset.IsImage); + Assert.Equal(312, asset.PixelHeight); + Assert.Equal(312, asset.PixelWidth); + Assert.Equal(312L, asset.Metadata["pixelHeight"]); + Assert.Equal(312L, asset.Metadata["pixelWidth"]); + Assert.Equal(AssetType.Image, asset.Type); + } + + [Fact] + public async Task Should_upload_image_gif_without_extension() + { + var asset = await _.UploadFileAsync("Assets/SampleGIFImage_40kbmb.gif", "image/gif", Guid.NewGuid().ToString()); + + // Should create metadata. + Assert.True(asset.IsImage); + Assert.Equal(312, asset.PixelHeight); + Assert.Equal(312, asset.PixelWidth); + Assert.Equal(312L, asset.Metadata["pixelHeight"]); + Assert.Equal(312L, asset.Metadata["pixelWidth"]); + Assert.Equal(AssetType.Image, asset.Type); + } + + [Fact] + public async Task Should_upload_image_png() + { + var asset = await _.UploadFileAsync("Assets/SamplePNGImage_100kbmb.png", "image/png"); + + // Should create metadata. + Assert.True(asset.IsImage); + Assert.Equal(170, asset.PixelHeight); + Assert.Equal(272, asset.PixelWidth); + Assert.Equal(170L, asset.Metadata["pixelHeight"]); + Assert.Equal(272L, asset.Metadata["pixelWidth"]); + Assert.Equal(AssetType.Image, asset.Type); + } + + [Fact] + public async Task Should_upload_image_jpg() + { + var asset = await _.UploadFileAsync("Assets/SampleJPGImage_50kbmb.jpg", "image/jpg"); + + // Should create metadata. + Assert.True(asset.IsImage); + Assert.Equal(300, asset.PixelHeight); + Assert.Equal(300, asset.PixelWidth); + Assert.Equal(300L, asset.Metadata["pixelHeight"]); + Assert.Equal(300L, asset.Metadata["pixelWidth"]); + Assert.Equal(96L, asset.Metadata["imageQuality"]); + Assert.Equal(AssetType.Image, asset.Type); + } + + [Fact] + public async Task Should_upload_audio_mp3() + { + var asset = await _.UploadFileAsync("Assets/SampleAudio_0.4mb.mp3", "audio/mp3"); + + // Should create metadata. + Assert.False(asset.IsImage); + Assert.Equal("00:00:28.2708750", asset.Metadata["duration"]); + Assert.Equal(128L, asset.Metadata["audioBitrate"]); + Assert.Equal(2L, asset.Metadata["audioChannels"]); + Assert.Equal(44100L, asset.Metadata["audioSampleRate"]); + Assert.Equal(AssetType.Audio, asset.Type); + } + + [Fact] + public async Task Should_upload_video_mp4() + { + var asset = await _.UploadFileAsync("Assets/SampleVideo_1280x720_1mb.mp4", "audio/mp4"); + + // Should create metadata. + Assert.False(asset.IsImage); + Assert.Equal("00:00:05.3120000", asset.Metadata["duration"]); + Assert.Equal(384L, asset.Metadata["audioBitrate"]); + Assert.Equal(2L, asset.Metadata["audioChannels"]); + Assert.Equal(48000L, asset.Metadata["audioSampleRate"]); + Assert.Equal(1280L, asset.Metadata["videoWidth"]); + Assert.Equal(720L, asset.Metadata["videoHeight"]); + Assert.Equal(AssetType.Video, asset.Type); + } + + [Fact] + public async Task Should_upload_video_mkv() + { + var asset = await _.UploadFileAsync("Assets/SampleVideo_1280x720_1mb.flv", "audio/webm"); + + // Should create metadata. + Assert.Equal(AssetType.Unknown, asset.Type); + } + + [Fact] + public async Task Should_upload_video_flv() + { + var asset = await _.UploadFileAsync("Assets/SampleVideo_1280x720_1mb.flv", "audio/x-flv"); + + // Should create metadata. + Assert.Equal(AssetType.Unknown, asset.Type); + } + + [Fact] + public async Task Should_upload_video_3gp() + { + var asset = await _.UploadFileAsync("Assets/SampleVideo_176x144_1mb.3gp", "audio/3gpp"); + + // Should create metadata. + Assert.Equal(AssetType.Unknown, asset.Type); + } + } +} diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/AssetTests.cs b/backend/tools/TestSuite/TestSuite.ApiTests/AssetTests.cs index 8e9ad41a5..c03c43bec 100644 --- a/backend/tools/TestSuite/TestSuite.ApiTests/AssetTests.cs +++ b/backend/tools/TestSuite/TestSuite.ApiTests/AssetTests.cs @@ -10,7 +10,6 @@ using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Threading.Tasks; -using Squidex.ClientLibrary; using Squidex.ClientLibrary.Management; using TestSuite.Fixtures; using Xunit; @@ -29,82 +28,19 @@ namespace TestSuite.ApiTests _ = fixture; } - [Fact] - public async Task Should_upload_image() - { - var fileName = $"{Guid.NewGuid()}.png"; - - using (var stream = new FileStream("Assets/logo-squared.png", FileMode.Open)) - { - var file = new FileParameter(stream, fileName, "image/png"); - - var asset = await _.Assets.PostAssetAsync(_.AppName, file); - - // Should create metadata. - Assert.True(asset.IsImage); - Assert.Equal(600, asset.PixelHeight); - Assert.Equal(600, asset.PixelWidth); - Assert.Equal(600L, asset.Metadata["pixelHeight"]); - Assert.Equal(600L, asset.Metadata["pixelWidth"]); - } - } - - [Fact] - public async Task Should_upload_image_without_extension() - { - var fileName = $"{Guid.NewGuid()}.png"; - - using (var stream = new FileStream("Assets/logo-squared.png", FileMode.Open)) - { - var file = new FileParameter(stream, fileName, "image/png"); - - var asset = await _.Assets.PostAssetAsync(_.AppName, file); - - // Should create metadata. - Assert.True(asset.IsImage); - Assert.Equal(600, asset.PixelHeight); - Assert.Equal(600, asset.PixelWidth); - Assert.Equal(600L, asset.Metadata["pixelHeight"]); - Assert.Equal(600L, asset.Metadata["pixelWidth"]); - } - } - [Fact] public async Task Should_replace_asset() { - var fileName = $"{Guid.NewGuid()}.png"; - - AssetDto asset; - // STEP 1: Create asset - using (var stream = new FileStream("Assets/logo-squared.png", FileMode.Open)) - { - var file = new FileParameter(stream, fileName, "image/png"); - - asset = await _.Assets.PostAssetAsync(_.AppName, file); - - Assert.True(asset.IsImage); - Assert.Equal(600, asset.PixelHeight); - Assert.Equal(600, asset.PixelWidth); - } + var asset = await _.UploadFileAsync("Assets/logo-squared.png", "image/png"); // STEP 2: Reupload asset - using (var stream = new FileStream("Assets/logo-wide.png", FileMode.Open)) - { - var file = new FileParameter(stream, fileName, "image/png"); - - asset = await _.Assets.PutAssetContentAsync(_.AppName, asset.Id.ToString(), file); - - // Should update metadata. - Assert.True(asset.IsImage); - Assert.Equal(135, asset.PixelHeight); - Assert.Equal(600, asset.PixelWidth); - } + asset = await _.UploadFileAsync("Assets/logo-wide.png", asset); using (var stream = new FileStream("Assets/logo-wide.png", FileMode.Open)) { - var downloaded = await DownloadAsync(asset); + var downloaded = await _.DownloadAsync(asset); // Should dowload with correct size. Assert.Equal(stream.Length, downloaded.Length); @@ -114,17 +50,8 @@ namespace TestSuite.ApiTests [Fact] public async Task Should_annote_asset() { - var fileName = $"{Guid.NewGuid()}.png"; - - AssetDto asset; - // STEP 1: Create asset - using (var stream = new FileStream("Assets/logo-squared.png", FileMode.Open)) - { - var file = new FileParameter(stream, fileName, "image/png"); - - asset = await _.Assets.PostAssetAsync(_.AppName, file); - } + var asset = await _.UploadFileAsync("Assets/logo-squared.png", "image/png"); // STEP 2: Annotate metadata. @@ -166,21 +93,14 @@ namespace TestSuite.ApiTests { var fileName = $"{Guid.NewGuid()}.png"; - AssetDto asset; - // STEP 1: Create asset - using (var stream = new FileStream("Assets/logo-squared.png", FileMode.Open)) - { - var file = new FileParameter(stream, fileName, "image/png"); - - asset = await _.Assets.PostAssetAsync(_.AppName, file); - } + var asset = await _.UploadFileAsync("Assets/logo-squared.png", "image/png"); // STEP 2: Download asset using (var stream = new FileStream("Assets/logo-squared.png", FileMode.Open)) { - var downloaded = await DownloadAsync(asset); + var downloaded = await _.DownloadAsync(asset); // Should dowload with correct size. Assert.Equal(stream.Length, downloaded.Length); @@ -211,7 +131,7 @@ namespace TestSuite.ApiTests // STEP 5: Download asset without key. using (var stream = new FileStream("Assets/logo-squared.png", FileMode.Open)) { - var ex = await Assert.ThrowsAsync(() => DownloadAsync(asset)); + var ex = await Assert.ThrowsAsync(() => _.DownloadAsync(asset)); // Should return 403 when not authenticated. Assert.Contains("403", ex.Message); @@ -221,17 +141,8 @@ namespace TestSuite.ApiTests [Fact] public async Task Should_delete_asset() { - var fileName = $"{Guid.NewGuid()}.png"; - - AssetDto asset; - // STEP 1: Create asset - using (var stream = new FileStream("Assets/logo-squared.png", FileMode.Open)) - { - var file = new FileParameter(stream, fileName, "image/png"); - - asset = await _.Assets.PostAssetAsync(_.AppName, file); - } + var asset = await _.UploadFileAsync("Assets/logo-squared.png", "image/png"); // STEP 2: Delete asset @@ -242,26 +153,5 @@ namespace TestSuite.ApiTests Assert.Equal(404, ex.StatusCode); } - - private async Task DownloadAsync(AssetDto asset) - { - var temp = new MemoryStream(); - - using (var client = new HttpClient()) - { - var url = $"{_.ServerUrl}{asset._links["content"].Href}"; - - var response = await client.GetAsync(url); - - response.EnsureSuccessStatusCode(); - - using (var stream = await response.Content.ReadAsStreamAsync()) - { - await stream.CopyToAsync(temp); - } - } - - return temp; - } } } diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleAudio_0.4mb.mp3 b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleAudio_0.4mb.mp3 new file mode 100644 index 000000000..084a7d1f8 Binary files /dev/null and b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleAudio_0.4mb.mp3 differ diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleGIFImage_40kbmb.gif b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleGIFImage_40kbmb.gif new file mode 100644 index 000000000..d41b6a157 Binary files /dev/null and b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleGIFImage_40kbmb.gif differ diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleJPGImage_50kbmb.jpg b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleJPGImage_50kbmb.jpg new file mode 100644 index 000000000..3a52a95df Binary files /dev/null and b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleJPGImage_50kbmb.jpg differ diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SamplePNGImage_100kbmb.png b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SamplePNGImage_100kbmb.png new file mode 100644 index 000000000..22392090a Binary files /dev/null and b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SamplePNGImage_100kbmb.png differ diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_1280x720_1mb.flv b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_1280x720_1mb.flv new file mode 100644 index 000000000..32b43bfc6 Binary files /dev/null and b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_1280x720_1mb.flv differ diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_1280x720_1mb.mkv b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_1280x720_1mb.mkv new file mode 100644 index 000000000..3fa72285a Binary files /dev/null and b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_1280x720_1mb.mkv differ diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_1280x720_1mb.mp4 b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_1280x720_1mb.mp4 new file mode 100644 index 000000000..ed139d6d5 Binary files /dev/null and b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_1280x720_1mb.mp4 differ diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_176x144_1mb.3gp b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_176x144_1mb.3gp new file mode 100644 index 000000000..7b2e319e9 Binary files /dev/null and b/backend/tools/TestSuite/TestSuite.ApiTests/Assets/SampleVideo_176x144_1mb.3gp differ diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/LanguagesTests.cs b/backend/tools/TestSuite/TestSuite.ApiTests/LanguagesTests.cs new file mode 100644 index 000000000..5aaf19cf3 --- /dev/null +++ b/backend/tools/TestSuite/TestSuite.ApiTests/LanguagesTests.cs @@ -0,0 +1,34 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using TestSuite.Fixtures; +using Xunit; + +#pragma warning disable SA1300 // Element should begin with upper-case letter +#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row + +namespace TestSuite.ApiTests +{ + public class LanguagesTests : IClassFixture + { + public ClientFixture _ { get; } + + public LanguagesTests(ClientFixture fixture) + { + _ = fixture; + } + + [Fact] + public async Task Should_provide_languages() + { + var languages = await _.Languages.GetLanguagesAsync(); + + Assert.True(languages.Count > 100); + } + } +} diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/PingTests.cs b/backend/tools/TestSuite/TestSuite.ApiTests/PingTests.cs new file mode 100644 index 000000000..0f95ce63f --- /dev/null +++ b/backend/tools/TestSuite/TestSuite.ApiTests/PingTests.cs @@ -0,0 +1,46 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System.Threading.Tasks; +using TestSuite.Fixtures; +using Xunit; + +#pragma warning disable SA1300 // Element should begin with upper-case letter +#pragma warning disable SA1507 // Code should not contain multiple blank lines in a row + +namespace TestSuite.ApiTests +{ + public class PingTests : IClassFixture + { + public CreatedAppFixture _ { get; } + + public PingTests(CreatedAppFixture fixture) + { + _ = fixture; + } + + [Fact] + public async Task Should_ping_service() + { + await _.Ping.GetPingAsync(); + } + + [Fact] + public async Task Should_ping_app() + { + await _.Ping.GetAppPingAsync(_.AppName); + } + + [Fact] + public async Task Should_get_info() + { + var infos = await _.Ping.GetInfoAsync(); + + Assert.NotNull(infos); + } + } +} diff --git a/backend/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj b/backend/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj index 14596b2da..ce21788bc 100644 --- a/backend/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj +++ b/backend/tools/TestSuite/TestSuite.ApiTests/TestSuite.ApiTests.csproj @@ -32,5 +32,29 @@ PreserveNewest + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + diff --git a/backend/tools/TestSuite/TestSuite.Shared/Fixtures/AssetFixture.cs b/backend/tools/TestSuite/TestSuite.Shared/Fixtures/AssetFixture.cs index bffb4fc09..be68f6d95 100644 --- a/backend/tools/TestSuite/TestSuite.Shared/Fixtures/AssetFixture.cs +++ b/backend/tools/TestSuite/TestSuite.Shared/Fixtures/AssetFixture.cs @@ -5,6 +5,10 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; using Squidex.ClientLibrary.Management; namespace TestSuite.Fixtures @@ -17,5 +21,57 @@ namespace TestSuite.Fixtures { Assets = ClientManager.CreateAssetsClient(); } + + public async Task DownloadAsync(AssetDto asset) + { + var temp = new MemoryStream(); + + using (var client = new HttpClient()) + { + var url = $"{ServerUrl}{asset._links["content"].Href}"; + + var response = await client.GetAsync(url); + + response.EnsureSuccessStatusCode(); + + using (var stream = await response.Content.ReadAsStreamAsync()) + { + await stream.CopyToAsync(temp); + } + } + + return temp; + } + + public async Task UploadFileAsync(string path, AssetDto asset, string fileName = null) + { + var fileInfo = new FileInfo(path); + + using (var stream = fileInfo.OpenRead()) + { + var upload = new FileParameter(stream, fileName ?? RandomName(fileInfo), asset.MimeType); + + return await Assets.PutAssetContentAsync(AppName, asset.Id.ToString(), upload); + } + } + + public async Task UploadFileAsync(string path, string mimeType, string fileName = null) + { + var fileInfo = new FileInfo(path); + + using (var stream = fileInfo.OpenRead()) + { + var upload = new FileParameter(stream, fileName ?? RandomName(fileInfo), mimeType); + + return await Assets.PostAssetAsync(AppName, upload); + } + } + + private static string RandomName(FileInfo fileInfo) + { + var fileName = $"{Guid.NewGuid()}{fileInfo.Extension}"; + + return fileName; + } } } diff --git a/backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientFixture.cs b/backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientFixture.cs index 9f39775f5..a618adc78 100644 --- a/backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientFixture.cs +++ b/backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientFixture.cs @@ -5,34 +5,22 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== -using System; -using Squidex.ClientLibrary; using Squidex.ClientLibrary.Management; -using TestSuite.Model; namespace TestSuite.Fixtures { - public class ClientFixture : IDisposable + public class ClientFixture : ClientManagerFixture { - public SquidexClientManager ClientManager { get; } + public IAppsClient Apps => ClientManager.CreateAppsClient(); - public IAppsClient Apps { get; } + public IBackupsClient Backups => ClientManager.CreateBackupsClient(); - public IBackupsClient Backups { get; } + public ILanguagesClient Languages => ClientManager.CreateLanguagesClient(); - public string ServerUrl { get; } = TestClient.ServerUrl; + public IPingClient Ping => ClientManager.CreatePingClient(); - public ClientFixture() - { - ClientManager = TestClient.ClientManager; + public IRulesClient Rules => ClientManager.CreateRulesClient(); - Apps = ClientManager.CreateAppsClient(); - - Backups = ClientManager.CreateBackupsClient(); - } - - public virtual void Dispose() - { - } + public ISchemasClient Schemas => ClientManager.CreateSchemasClient(); } } diff --git a/backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientManagerFixture.cs b/backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientManagerFixture.cs new file mode 100644 index 000000000..901567c7c --- /dev/null +++ b/backend/tools/TestSuite/TestSuite.Shared/Fixtures/ClientManagerFixture.cs @@ -0,0 +1,37 @@ +// ========================================================================== +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex UG (haftungsbeschraenkt) +// All rights reserved. Licensed under the MIT license. +// ========================================================================== + +using System; +using Squidex.ClientLibrary; + +namespace TestSuite.Fixtures +{ + public class ClientManagerFixture : IDisposable + { + public string ServerUrl { get; } = "http://localhost:5000"; + + public string ClientId { get; } = "root"; + + public string ClientSecret { get; } = "xeLd6jFxqbXJrfmNLlO2j1apagGGGSyZJhFnIuHp4I0="; + + public string AppName { get; } = "integration-tests"; + + public SquidexClientManager ClientManager { get; } + + public ClientManagerFixture() + { + ClientManager = new SquidexClientManager(ServerUrl, AppName, ClientId, ClientSecret) + { + ReadResponseAsString = true + }; + } + + public virtual void Dispose() + { + } + } +} diff --git a/backend/tools/TestSuite/TestSuite.Shared/Fixtures/ContentFixture.cs b/backend/tools/TestSuite/TestSuite.Shared/Fixtures/ContentFixture.cs index 9ae4eda75..5572478fa 100644 --- a/backend/tools/TestSuite/TestSuite.Shared/Fixtures/ContentFixture.cs +++ b/backend/tools/TestSuite/TestSuite.Shared/Fixtures/ContentFixture.cs @@ -38,7 +38,7 @@ namespace TestSuite.Fixtures { var schemas = ClientManager.CreateSchemasClient(); - await schemas.PostSchemaAsync(TestClient.TestAppName, new CreateSchemaDto + await schemas.PostSchemaAsync(AppName, new CreateSchemaDto { Name = SchemaName, Fields = new List diff --git a/backend/tools/TestSuite/TestSuite.Shared/Fixtures/CreatedAppFixture.cs b/backend/tools/TestSuite/TestSuite.Shared/Fixtures/CreatedAppFixture.cs index ee70259cf..322d04b7c 100644 --- a/backend/tools/TestSuite/TestSuite.Shared/Fixtures/CreatedAppFixture.cs +++ b/backend/tools/TestSuite/TestSuite.Shared/Fixtures/CreatedAppFixture.cs @@ -7,14 +7,11 @@ using System.Threading.Tasks; using Squidex.ClientLibrary.Management; -using TestSuite.Model; namespace TestSuite.Fixtures { public class CreatedAppFixture : ClientFixture { - public string AppName { get; } = TestClient.TestAppName; - public CreatedAppFixture() { Task.Run(async () => diff --git a/backend/tools/TestSuite/TestSuite.Shared/Model/TestClient.cs b/backend/tools/TestSuite/TestSuite.Shared/Model/TestClient.cs deleted file mode 100644 index cbea32faa..000000000 --- a/backend/tools/TestSuite/TestSuite.Shared/Model/TestClient.cs +++ /dev/null @@ -1,31 +0,0 @@ -// ========================================================================== -// Squidex Headless CMS -// ========================================================================== -// Copyright (c) Squidex UG (haftungsbeschraenkt) -// All rights reserved. Licensed under the MIT license. -// ========================================================================== - -using Squidex.ClientLibrary; - -namespace TestSuite.Model -{ - internal static class TestClient - { - public const string ServerUrl = "http://localhost:5000"; - - public const string ClientId = "root"; - public const string ClientSecret = "xeLd6jFxqbXJrfmNLlO2j1apagGGGSyZJhFnIuHp4I0="; - - public const string TestAppName = "integration-tests"; - - public static readonly SquidexClientManager ClientManager = - new SquidexClientManager( - ServerUrl, - TestAppName, - ClientId, - ClientSecret) - { - ReadResponseAsString = true - }; - } -} \ No newline at end of file diff --git a/backend/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj b/backend/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj index 869feea5d..676f7dd9f 100644 --- a/backend/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj +++ b/backend/tools/TestSuite/TestSuite.Shared/TestSuite.Shared.csproj @@ -4,7 +4,6 @@ - @@ -15,4 +14,7 @@ + + + diff --git a/backend/tools/TestSuite/TestSuite.sln b/backend/tools/TestSuite/TestSuite.sln index 8bc1f5817..216b4ccc8 100644 --- a/backend/tools/TestSuite/TestSuite.sln +++ b/backend/tools/TestSuite/TestSuite.sln @@ -9,6 +9,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestSuite.ApiTests", "TestS EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestSuite.LoadTests", "TestSuite.LoadTests\TestSuite.LoadTests.csproj", "{F37572D9-4880-40F4-B3CB-83F58A40CA48}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Squidex.ClientLibrary", "..\..\..\..\squidex-samples\csharp\Squidex.ClientLibrary\Squidex.ClientLibrary\Squidex.ClientLibrary.csproj", "{9AF5F7FD-3C82-4A4F-8494-739911E2BB6F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {F37572D9-4880-40F4-B3CB-83F58A40CA48}.Debug|Any CPU.Build.0 = Debug|Any CPU {F37572D9-4880-40F4-B3CB-83F58A40CA48}.Release|Any CPU.ActiveCfg = Release|Any CPU {F37572D9-4880-40F4-B3CB-83F58A40CA48}.Release|Any CPU.Build.0 = Release|Any CPU + {9AF5F7FD-3C82-4A4F-8494-739911E2BB6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AF5F7FD-3C82-4A4F-8494-739911E2BB6F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AF5F7FD-3C82-4A4F-8494-739911E2BB6F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AF5F7FD-3C82-4A4F-8494-739911E2BB6F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE