From c77dc5dc00875b06cc435099c7d0b6709b69b21a Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 19 Oct 2021 23:11:54 +1100 Subject: [PATCH] Migrating tests that depend on Published Cache from the old test project (#11242) * starts cleaning up old test project, removing ones we'll never convert, moves new test to where it should be. * Makes ContentNodeKit immutable properties, moves first nucache tests over * Gets the Nucache unit tests working and refactors a bit to use builder pattern for models. * Migrates first xml based cache test to use nucache. * Migrates a bunch more * Migrates remaining tests for PublishedContentTests * Moves PublishedRouterTests * Moves PublishedContentExtensionTests * Moves more tests. * committing wip * committing wip * Gets PublishedContentLanguageVariantTests converted and working. * Fixes DataTable ext method and moves PublishedContentDataTableTests * Moves PublishedMediaTests * wip - moving EntityXmlSerializerTests * Moves more tests * moves more tests * moves more tests * Move another test * Moves more tests * Fix test * move another test * Moves more tests * Moves more tests * Moves more tests * wip before merge * More tests * More tests * More tests * More tests * More tests * More tests * Cleanup and moving classes. * Remove unused code * Fixed failing tests, due to new null checks, that did not exist in v8 * Avoid breaking changes * Unbreak more things, even that it the old solution was crazy.. * Fixed bug where ordering of stream readings was changed.. * cleanup Co-authored-by: Bjarke Berg --- .../Cache/FastDictionaryAppCache.cs | 4 +- .../Extensions/PublishedContentExtensions.cs | 4 +- .../PropertyEditors/VoidEditor.cs | 4 +- .../Internal/InternalPublishedContent.cs | 4 +- .../Internal/InternalPublishedContentCache.cs | 3 + .../Internal/InternalPublishedProperty.cs | 5 +- .../Internal/InternalPublishedSnapshot.cs | 4 + .../InternalPublishedSnapshotService.cs | 3 + .../Routing/DefaultUrlProvider.cs | 160 +- src/Umbraco.Core/Routing/SiteDomainMapper.cs | 175 +- .../Routing/UrlProviderExtensions.cs | 132 +- .../PublishedContentTypeCache.cs | 55 +- .../ContentCache.cs | 169 +- .../ContentNode.cs | 9 +- .../ContentNodeKit.cs | 33 +- .../ContentStore.cs | 4 + .../DataSource/BTree.ContentDataSerializer.cs | 30 +- .../BTree.ContentNodeKitSerializer.cs | 32 +- .../DataSource/ContentData.cs | 37 +- .../DomainCacheExtensions.cs | 15 + .../Persistence/NuCacheContentRepository.cs | 80 +- .../PublishedMember.cs | 11 +- .../PublishedSnapshotService.cs | 3 - .../ConfigureBackOfficeCookieOptions.cs | 1 - .../Builders/ContentBuilder.cs | 7 +- .../Builders/ContentDataBuilder.cs | 221 ++ .../Builders/ContentNodeKitBuilder.cs | 96 + .../Interfaces/IWithAllowAsRootBuilder.cs | 7 + .../Builders/PropertyDataBuilder.cs | 36 + .../Published/PublishedContentXml.cs | 152 ++ .../Published/PublishedContentXmlAdapter.cs | 146 ++ .../Published/PublishedSnapshotTestObjects.cs | 1 + .../AutoPublishedContentType.cs | 68 - .../PublishedContent/ContentType2.cs | 20 - .../PublishedContent/ContentType2Sub.cs | 16 - ...alPublishedPropertyWithLanguageVariants.cs | 84 - .../PublishedContentStrong1.cs | 19 - .../PublishedContentStrong1Sub.cs | 19 - .../PublishedContentStrong2.cs | 19 - .../TestLastChanceFinder.cs | 12 + .../TestPublishedSnapshotAccessor.cs | 8 +- .../ContentTypeServiceVariantsTests.cs | 7 +- .../Services/EntityXmlSerializerTests.cs | 141 +- .../Umbraco.Tests.Integration.csproj | 2 + .../UrlAndDomains/DomainAndUrlsTests.cs | 153 ++ .../UrlAndDomains/package.xml | 77 + .../PublishedSnapshotServiceTestBase.cs | 278 +++ .../TestHelpers/TestNuCacheContentService.cs | 95 + .../Collections/StackQueueTests.cs | 6 +- .../Routing/ContentFinderByAliasTests.cs | 56 + .../ContentFinderByAliasWithDomainsTests.cs | 44 +- .../Routing/ContentFinderByIdTests.cs | 57 + .../ContentFinderByPageIdQueryTests.cs | 63 + .../ContentFinderByUrlAndTemplateTests.cs | 85 + .../Routing/ContentFinderByUrlTests.cs | 167 ++ .../ContentFinderByUrlWithDomainsTests.cs | 66 +- .../Routing/DomainsAndCulturesTests.cs | 283 +-- .../Routing/GetContentUrlsTests.cs | 216 ++ .../Routing/PublishedRouterTests.cs | 88 + ...oviderWithHideTopLevelNodeFromPathTests.cs | 57 + ...derWithoutHideTopLevelNodeFromPathTests.cs | 336 +++ .../Routing/UrlRoutingTestBase.cs} | 88 +- .../Routing/UrlsProviderWithDomainsTests.cs | 315 ++- .../Routing/UrlsWithNestedDomains.cs | 75 +- .../ContentSerializationTests.cs | 28 +- .../PublishedContentCacheTests.cs | 85 + .../PublishedContentDataTableTests.cs | 200 ++ .../PublishedContentExtensionTests.cs | 77 + .../PublishedContentLanguageVariantTests.cs | 346 +++ .../PublishedCache}/PublishedContentTests.cs | 611 +++-- .../PublishedCache/PublishedMediaTests.cs | 242 ++ ...ublishedSnapshotServiceCollectionTests.cs} | 705 ++---- .../PublishedSnapshotServiceContentTests.cs | 198 ++ .../PublishedCache/RootNodeTests.cs | 52 + .../PublishedCache}/UrlRoutesTests.cs | 159 +- .../AutoInterningStringConverterTests.cs | 11 +- .../PublishedContentCacheTests.cs | 133 -- .../PublishedMediaCacheTests.cs | 413 ---- tests/Umbraco.Tests/Issues/U9560.cs | 100 - .../LegacyXmlPublishedCache/ContentXmlDto.cs | 24 - .../DictionaryPublishedContent.cs | 221 -- .../LegacyXmlPublishedCache/DomainCache.cs | 37 - .../BackgroundTaskRunner.cs | 863 ------- .../BackgroundTaskRunnerOptions.cs | 53 - .../LegacyBackgroundTask/IBackgroundTask.cs | 30 - .../IBackgroundTaskRunner.cs | 20 - .../ILatchedBackgroundTask.cs | 32 - .../LatchedBackgroundTaskBase.cs | 61 - .../LegacyBackgroundTask/TaskEventArgs.cs | 42 - .../ThreadingTaskImmutable.cs | 44 - .../LegacyXmlPublishedCache/PreviewContent.cs | 164 -- .../LegacyXmlPublishedCache/PreviewXmlDto.cs | 24 - .../PublishedContentCache.cs | 548 ----- .../PublishedMediaCache.cs | 698 ------ .../PublishedMember.cs | 154 -- .../PublishedMemberCache.cs | 35 - .../PublishedSnapshot.cs | 63 - .../LegacyXmlPublishedCache/RoutesCache.cs | 112 - .../SafeXmlReaderWriter.cs | 154 -- .../UmbracoContextCache.cs | 26 - .../XmlPublishedContent.cs | 441 ---- .../XmlPublishedProperty.cs | 76 - .../XmlPublishedSnapshotService.cs | 222 -- .../LegacyXmlPublishedCache/XmlStore.cs | 2058 ----------------- .../XmlStoreFilePersister.cs | 181 -- tests/Umbraco.Tests/Models/ContentXmlTest.cs | 65 - tests/Umbraco.Tests/Models/MediaXmlTest.cs | 84 - .../FaultHandling/ConnectionRetryTest.cs | 45 - .../Persistence/Mappers/MapperTestBase.cs | 24 - .../NPocoTests/PetaPocoCachesTest.cs | 195 -- .../Querying/ContentTypeSqlMappingTests.cs | 199 -- .../Plugins/PluginManagerTests.cs | 392 ---- .../PublishedContent/NuCacheTests.cs | 316 --- .../PublishedContentDataTableTests.cs | 182 -- .../PublishedContentExtensionTests.cs | 104 - .../PublishedContentLanguageVariantTests.cs | 363 --- .../PublishedContentMoreTests.cs | 215 -- .../PublishedContentSnapshotTestBase.cs | 104 - .../PublishedContentTestBase.cs | 84 - .../PublishedContent/PublishedMediaTests.cs | 511 ---- .../PublishedContent/PublishedRouterTests.cs | 57 - .../PublishedContent/RootNodeTests.cs | 31 - .../SolidPublishedSnapshot.cs | 449 ---- .../StronglyTypedModels/Home.cs | 16 - .../Routing/ContentFinderByIdTests.cs | 32 - .../ContentFinderByPageIdQueryTests.cs | 35 - .../ContentFinderByUrlAndTemplateTests.cs | 57 - .../Routing/ContentFinderByUrlTests.cs | 156 -- .../Routing/GetContentUrlsTests.cs | 182 -- .../Routing/RouteTestExtensions.cs | 58 - .../Umbraco.Tests/Routing/RoutesCacheTests.cs | 45 - ...oviderWithHideTopLevelNodeFromPathTests.cs | 59 - ...derWithoutHideTopLevelNodeFromPathTests.cs | 311 --- .../Routing/UrlRoutingTestBase.cs | 65 - .../PassThroughEventDispatcherTests.cs | 59 - tests/Umbraco.Tests/Scoping/ScopedXmlTests.cs | 303 --- .../Services/Importing/Dictionary-Package.xml | 36 - .../Services/TestWithSomeContentBase.cs | 52 - .../TestHelpers/BaseUsingSqlCeSyntax.cs | 81 - .../Umbraco.Tests/TestHelpers/BaseWebTest.cs | 118 - .../AuthenticateEverythingExtensions.cs | 18 - .../AuthenticateEverythingMiddleware.cs | 65 - .../SpecificAssemblyResolver.cs | 21 - .../TestControllerActivator.cs | 23 - .../TestControllerActivatorBase.cs | 157 -- .../ControllerTesting/TestStartup.cs | 55 - .../ControllerTesting/TraceExceptionLogger.cs | 16 - .../TestHelpers/Entities/MockedContent.cs | 167 -- .../Entities/MockedContentTypes.cs | 514 ---- .../TestHelpers/Entities/MockedEntity.cs | 26 - .../TestHelpers/Entities/MockedMedia.cs | 85 - .../TestHelpers/Entities/MockedMember.cs | 81 - .../Entities/MockedPropertyTypes.cs | 66 - .../TestHelpers/Entities/MockedUser.cs | 62 - .../TestHelpers/Entities/MockedUserGroup.cs | 36 - .../TestHelpers/FakeHttpContextFactory.cs | 120 - .../Stubs/TestControllerFactory.cs | 85 - .../TestHelpers/Stubs/TestExamineManager.cs | 45 - .../TestHelpers/Stubs/TestLastChanceFinder.cs | 9 - .../Stubs/TestUserPasswordConfig.cs | 24 - tests/Umbraco.Tests/TestHelpers/TestHelper.cs | 346 --- .../TestHelpers/TestObjects-Mocks.cs | 327 --- .../Umbraco.Tests/TestHelpers/TestObjects.cs | 95 - .../TestHelpers/TestWithDatabaseBase.cs | 429 ---- .../Accessors/NoHttpContextAccessor.cs | 10 - .../Testing/Objects/TestDataSource.cs | 67 - .../Umbraco.Tests/Testing/UmbracoTestBase.cs | 585 ----- tests/Umbraco.Tests/Umbraco.Tests.csproj | 197 +- .../Web/HttpCookieExtensionsTests.cs | 43 - .../Mvc/ViewDataDictionaryExtensionTests.cs | 40 - .../masterpages/site1/template2.master | 5 - 171 files changed, 5383 insertions(+), 17927 deletions(-) create mode 100644 src/Umbraco.PublishedCache.NuCache/DomainCacheExtensions.cs create mode 100644 tests/Umbraco.Tests.Common/Builders/ContentDataBuilder.cs create mode 100644 tests/Umbraco.Tests.Common/Builders/ContentNodeKitBuilder.cs create mode 100644 tests/Umbraco.Tests.Common/Builders/Interfaces/IWithAllowAsRootBuilder.cs create mode 100644 tests/Umbraco.Tests.Common/Builders/PropertyDataBuilder.cs create mode 100644 tests/Umbraco.Tests.Common/Published/PublishedContentXml.cs create mode 100644 tests/Umbraco.Tests.Common/Published/PublishedContentXmlAdapter.cs delete mode 100644 tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/AutoPublishedContentType.cs delete mode 100644 tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/ContentType2.cs delete mode 100644 tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/ContentType2Sub.cs delete mode 100644 tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/InternalPublishedPropertyWithLanguageVariants.cs delete mode 100644 tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong1.cs delete mode 100644 tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong1Sub.cs delete mode 100644 tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong2.cs create mode 100644 tests/Umbraco.Tests.Common/TestLastChanceFinder.cs create mode 100644 tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/DomainAndUrlsTests.cs create mode 100644 tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/package.xml create mode 100644 tests/Umbraco.Tests.UnitTests/TestHelpers/PublishedSnapshotServiceTestBase.cs create mode 100644 tests/Umbraco.Tests.UnitTests/TestHelpers/TestNuCacheContentService.cs rename tests/{Umbraco.Tests => Umbraco.Tests.UnitTests/Umbraco.Core}/Collections/StackQueueTests.cs (93%) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByAliasTests.cs rename tests/{Umbraco.Tests => Umbraco.Tests.UnitTests/Umbraco.Core}/Routing/ContentFinderByAliasWithDomainsTests.cs (52%) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByIdTests.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByPageIdQueryTests.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAndTemplateTests.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlTests.cs rename tests/{Umbraco.Tests => Umbraco.Tests.UnitTests/Umbraco.Core}/Routing/ContentFinderByUrlWithDomainsTests.cs (83%) rename tests/{Umbraco.Tests => Umbraco.Tests.UnitTests/Umbraco.Core}/Routing/DomainsAndCulturesTests.cs (72%) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/GetContentUrlsTests.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/PublishedRouterTests.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs rename tests/{Umbraco.Tests/Routing/ContentFinderByAliasTests.cs => Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlRoutingTestBase.cs} (81%) rename tests/{Umbraco.Tests => Umbraco.Tests.UnitTests/Umbraco.Core}/Routing/UrlsProviderWithDomainsTests.cs (56%) rename tests/{Umbraco.Tests => Umbraco.Tests.UnitTests/Umbraco.Core}/Routing/UrlsWithNestedDomains.cs (85%) rename tests/{Umbraco.Tests/PublishedContent => Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache}/ContentSerializationTests.cs (84%) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentCacheTests.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentDataTableTests.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentExtensionTests.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs rename tests/{Umbraco.Tests/PublishedContent => Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache}/PublishedContentTests.cs (51%) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedMediaTests.cs rename tests/{Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs => Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceCollectionTests.cs} (59%) create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceContentTests.cs create mode 100644 tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/RootNodeTests.cs rename tests/{Umbraco.Tests/Routing => Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache}/UrlRoutesTests.cs (68%) rename tests/{Umbraco.Tests => Umbraco.Tests.UnitTests/Umbraco.Infrastructure}/Serialization/AutoInterningStringConverterTests.cs (87%) delete mode 100644 tests/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs delete mode 100644 tests/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs delete mode 100644 tests/Umbraco.Tests/Issues/U9560.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/ContentXmlDto.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/BackgroundTaskRunner.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/BackgroundTaskRunnerOptions.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/IBackgroundTask.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/IBackgroundTaskRunner.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/ILatchedBackgroundTask.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/LatchedBackgroundTaskBase.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/TaskEventArgs.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/ThreadingTaskImmutable.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/PreviewContent.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/PreviewXmlDto.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMember.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshot.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/RoutesCache.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedProperty.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs delete mode 100644 tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs delete mode 100644 tests/Umbraco.Tests/Models/ContentXmlTest.cs delete mode 100644 tests/Umbraco.Tests/Models/MediaXmlTest.cs delete mode 100644 tests/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs delete mode 100644 tests/Umbraco.Tests/Persistence/Mappers/MapperTestBase.cs delete mode 100644 tests/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs delete mode 100644 tests/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs delete mode 100644 tests/Umbraco.Tests/Plugins/PluginManagerTests.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/NuCacheTests.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/RootNodeTests.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs delete mode 100644 tests/Umbraco.Tests/PublishedContent/StronglyTypedModels/Home.cs delete mode 100644 tests/Umbraco.Tests/Routing/ContentFinderByIdTests.cs delete mode 100644 tests/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs delete mode 100644 tests/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs delete mode 100644 tests/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs delete mode 100644 tests/Umbraco.Tests/Routing/GetContentUrlsTests.cs delete mode 100644 tests/Umbraco.Tests/Routing/RouteTestExtensions.cs delete mode 100644 tests/Umbraco.Tests/Routing/RoutesCacheTests.cs delete mode 100644 tests/Umbraco.Tests/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs delete mode 100644 tests/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs delete mode 100644 tests/Umbraco.Tests/Routing/UrlRoutingTestBase.cs delete mode 100644 tests/Umbraco.Tests/Scoping/PassThroughEventDispatcherTests.cs delete mode 100644 tests/Umbraco.Tests/Scoping/ScopedXmlTests.cs delete mode 100644 tests/Umbraco.Tests/Services/Importing/Dictionary-Package.xml delete mode 100644 tests/Umbraco.Tests/Services/TestWithSomeContentBase.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/BaseWebTest.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingExtensions.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/ControllerTesting/SpecificAssemblyResolver.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/ControllerTesting/TraceExceptionLogger.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Entities/MockedEntity.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Entities/MockedMember.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Entities/MockedPropertyTypes.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Stubs/TestLastChanceFinder.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/Stubs/TestUserPasswordConfig.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/TestHelper.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/TestObjects.cs delete mode 100644 tests/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs delete mode 100644 tests/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs delete mode 100644 tests/Umbraco.Tests/Testing/Objects/TestDataSource.cs delete mode 100644 tests/Umbraco.Tests/Testing/UmbracoTestBase.cs delete mode 100644 tests/Umbraco.Tests/Web/HttpCookieExtensionsTests.cs delete mode 100644 tests/Umbraco.Tests/Web/Mvc/ViewDataDictionaryExtensionTests.cs delete mode 100644 tests/Umbraco.Tests/masterpages/site1/template2.master diff --git a/src/Umbraco.Core/Cache/FastDictionaryAppCache.cs b/src/Umbraco.Core/Cache/FastDictionaryAppCache.cs index ddd9f96c73a2..670574896ee1 100644 --- a/src/Umbraco.Core/Cache/FastDictionaryAppCache.cs +++ b/src/Umbraco.Core/Cache/FastDictionaryAppCache.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -18,6 +18,8 @@ public class FastDictionaryAppCache : IAppCache /// private readonly ConcurrentDictionary> _items = new ConcurrentDictionary>(); + public IEnumerable Keys => _items.Keys; + public int Count => _items.Count; /// diff --git a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs index 8c4e57244291..197f6f6d63fd 100644 --- a/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/PublishedContentExtensions.cs @@ -1328,8 +1328,8 @@ private static Dictionary GetAliasesAndNames(IContentTypeService {"NodeTypeAlias", "NodeTypeAlias"}, {"CreateDate", "CreateDate"}, {"UpdateDate", "UpdateDate"}, - {"CreatorName", "CreatorName"}, - {"WriterName", "WriterName"}, + {"CreatorId", "CreatorId"}, + {"WriterId", "WriterId"}, {"Url", "Url"} }; diff --git a/src/Umbraco.Core/PropertyEditors/VoidEditor.cs b/src/Umbraco.Core/PropertyEditors/VoidEditor.cs index 716e722be14c..8510c982ab03 100644 --- a/src/Umbraco.Core/PropertyEditors/VoidEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/VoidEditor.cs @@ -1,4 +1,4 @@ -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Serialization; @@ -16,8 +16,6 @@ namespace Umbraco.Cms.Core.PropertyEditors [HideFromTypeFinder] public class VoidEditor : DataEditor { - private readonly IJsonSerializer _jsonSerializer; - /// /// Initializes a new instance of the class. /// diff --git a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContent.cs b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContent.cs index 34152ebc0fca..2c79e68e1d8b 100644 --- a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContent.cs +++ b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContent.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PublishedCache.Internal { - + // TODO: Only used in unit tests, needs to be moved to test project + [EditorBrowsable(EditorBrowsableState.Never)] public sealed class InternalPublishedContent : IPublishedContent { public InternalPublishedContent(IPublishedContentType contentType) diff --git a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContentCache.cs b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContentCache.cs index 2a197affff57..c2f8f274193b 100644 --- a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContentCache.cs +++ b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedContentCache.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.Xml; namespace Umbraco.Cms.Core.PublishedCache.Internal { + // TODO: Only used in unit tests, needs to be moved to test project + [EditorBrowsable(EditorBrowsableState.Never)] public sealed class InternalPublishedContentCache : PublishedCacheBase, IPublishedContentCache, IPublishedMediaCache { private readonly Dictionary _content = new Dictionary(); diff --git a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedProperty.cs b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedProperty.cs index a98eff6484f2..7fe46d8d75ca 100644 --- a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedProperty.cs +++ b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedProperty.cs @@ -1,7 +1,10 @@ -using Umbraco.Cms.Core.Models.PublishedContent; +using System.ComponentModel; +using Umbraco.Cms.Core.Models.PublishedContent; namespace Umbraco.Cms.Core.PublishedCache.Internal { + // TODO: Only used in unit tests, needs to be moved to test project + [EditorBrowsable(EditorBrowsableState.Never)] public class InternalPublishedProperty : IPublishedProperty { public IPublishedPropertyType PropertyType { get; set; } diff --git a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedSnapshot.cs b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedSnapshot.cs index c73b6cef7692..1ff8d99139ca 100644 --- a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedSnapshot.cs +++ b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedSnapshot.cs @@ -1,8 +1,12 @@ using System; +using System.ComponentModel; using Umbraco.Cms.Core.Cache; namespace Umbraco.Cms.Core.PublishedCache.Internal { + + // TODO: Only used in unit tests, needs to be moved to test project + [EditorBrowsable(EditorBrowsableState.Never)] public sealed class InternalPublishedSnapshot : IPublishedSnapshot { public InternalPublishedContentCache InnerContentCache { get; } = new InternalPublishedContentCache(); diff --git a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedSnapshotService.cs b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedSnapshotService.cs index 5dcdc5189f7c..61e2a9c2a9b5 100644 --- a/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedSnapshotService.cs +++ b/src/Umbraco.Core/PublishedCache/Internal/InternalPublishedSnapshotService.cs @@ -1,10 +1,13 @@ using System.Collections.Generic; +using System.ComponentModel; using System.Threading.Tasks; using Umbraco.Cms.Core.Cache; using Umbraco.Extensions; namespace Umbraco.Cms.Core.PublishedCache.Internal { + // TODO: Only used in unit tests, needs to be moved to test project + [EditorBrowsable(EditorBrowsableState.Never)] public class InternalPublishedSnapshotService : IPublishedSnapshotService { private InternalPublishedSnapshot _snapshot; diff --git a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs index 16248854e584..ef7a29afaf55 100644 --- a/src/Umbraco.Core/Routing/DefaultUrlProvider.cs +++ b/src/Umbraco.Core/Routing/DefaultUrlProvider.cs @@ -1,116 +1,109 @@ using System; using System.Collections.Generic; using System.Globalization; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Web.Common.DependencyInjection; using Umbraco.Extensions; namespace Umbraco.Cms.Core.Routing { /// - /// Provides urls. + /// Provides urls. /// public class DefaultUrlProvider : IUrlProvider { - private readonly RequestHandlerSettings _requestSettings; + private readonly ILocalizationService _localizationService; + private readonly ILocalizedTextService _localizedTextService; private readonly ILogger _logger; + private readonly RequestHandlerSettings _requestSettings; private readonly ISiteDomainMapper _siteDomainMapper; private readonly IUmbracoContextAccessor _umbracoContextAccessor; private readonly UriUtility _uriUtility; - public DefaultUrlProvider(IOptions requestSettings, ILogger logger, ISiteDomainMapper siteDomainMapper, IUmbracoContextAccessor umbracoContextAccessor, UriUtility uriUtility) + [Obsolete("Use ctor with all parameters")] + public DefaultUrlProvider(IOptions requestSettings, ILogger logger, + ISiteDomainMapper siteDomainMapper, IUmbracoContextAccessor umbracoContextAccessor, UriUtility uriUtility) + : this(requestSettings, logger, siteDomainMapper, umbracoContextAccessor, uriUtility, + StaticServiceProvider.Instance.GetRequiredService()) + { + } + + public DefaultUrlProvider( + IOptions requestSettings, + ILogger logger, + ISiteDomainMapper siteDomainMapper, + IUmbracoContextAccessor umbracoContextAccessor, + UriUtility uriUtility, + ILocalizationService localizationService) { _requestSettings = requestSettings.Value; _logger = logger; _siteDomainMapper = siteDomainMapper; - _uriUtility = uriUtility; _umbracoContextAccessor = umbracoContextAccessor; + _uriUtility = uriUtility; + _localizationService = localizationService; } - #region GetUrl - - /// - public virtual UrlInfo GetUrl(IPublishedContent content, UrlMode mode, string culture, Uri current) - { - if (!current.IsAbsoluteUri) throw new ArgumentException("Current URL must be absolute.", nameof(current)); - var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - // will not use cache if previewing - var route = umbracoContext.Content.GetRouteById(content.Id, culture); - - return GetUrlFromRoute(route, umbracoContext, content.Id, current, mode, culture); - } - - internal UrlInfo GetUrlFromRoute(string route, IUmbracoContext umbracoContext, int id, Uri current, UrlMode mode, string culture) - { - if (string.IsNullOrWhiteSpace(route)) - { - _logger.LogDebug("Couldn't find any page with nodeId={NodeId}. This is most likely caused by the page not being published.", id); - return null; - } - - // extract domainUri and path - // route is / or / - var pos = route.IndexOf('/'); - var path = pos == 0 ? route : route.Substring(pos); - var domainUri = pos == 0 - ? null - : DomainUtilities.DomainForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, int.Parse(route.Substring(0, pos), CultureInfo.InvariantCulture), current, culture); - - // assemble the URL from domainUri (maybe null) and path - var url = AssembleUrl(domainUri, path, current, mode).ToString(); - - return UrlInfo.Url(url, culture); - } - - #endregion - #region GetOtherUrls /// - /// Gets the other URLs of a published content. + /// Gets the other URLs of a published content. /// /// The Umbraco context. /// The published content id. /// The current absolute URL. /// The other URLs for the published content. /// - /// Other URLs are those that GetUrl would not return in the current context, but would be valid - /// URLs for the node in other contexts (different domain for current request, umbracoUrlAlias...). + /// + /// Other URLs are those that GetUrl would not return in the current context, but would be valid + /// URLs for the node in other contexts (different domain for current request, umbracoUrlAlias...). + /// /// public virtual IEnumerable GetOtherUrls(int id, Uri current) { - var umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); - var node = umbracoContext.Content.GetById(id); + IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); + IPublishedContent node = umbracoContext.Content.GetById(id); if (node == null) { yield break; } // look for domains, walking up the tree - var n = node; - var domainUris = DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, n.Id, current, false); + IPublishedContent n = node; + IEnumerable domainUris = + DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, n.Id, + current, false); while (domainUris == null && n != null) // n is null at root { n = n.Parent; // move to parent node - domainUris = n == null ? null : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, n.Id, current, excludeDefault: true); + domainUris = n == null + ? null + : DomainUtilities.DomainsForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, n.Id, + current); } // no domains = exit - if (domainUris ==null) + if (domainUris == null) { yield break; } - foreach (var d in domainUris) + foreach (DomainAndUri d in domainUris) { var culture = d?.Culture; // although we are passing in culture here, if any node in this path is invariant, it ignores the culture anyways so this is ok var route = umbracoContext.Content.GetRouteById(id, culture); - if (route == null) continue; + if (route == null) + { + continue; + } // need to strip off the leading ID for the route if it exists (occurs if the route is for a node with a domain assigned) var pos = route.IndexOf('/'); @@ -124,9 +117,57 @@ public virtual IEnumerable GetOtherUrls(int id, Uri current) #endregion + #region GetUrl + + /// + public virtual UrlInfo GetUrl(IPublishedContent content, UrlMode mode, string culture, Uri current) + { + if (!current.IsAbsoluteUri) + { + throw new ArgumentException("Current URL must be absolute.", nameof(current)); + } + + IUmbracoContext umbracoContext = _umbracoContextAccessor.GetRequiredUmbracoContext(); + // will not use cache if previewing + var route = umbracoContext.Content.GetRouteById(content.Id, culture); + + return GetUrlFromRoute(route, umbracoContext, content.Id, current, mode, culture); + } + + internal UrlInfo GetUrlFromRoute(string route, IUmbracoContext umbracoContext, int id, Uri current, + UrlMode mode, string culture) + { + if (string.IsNullOrWhiteSpace(route)) + { + _logger.LogDebug( + "Couldn't find any page with nodeId={NodeId}. This is most likely caused by the page not being published.", + id); + return null; + } + + // extract domainUri and path + // route is / or / + var pos = route.IndexOf('/'); + var path = pos == 0 ? route : route.Substring(pos); + var domainUri = pos == 0 + ? null + : DomainUtilities.DomainForNode(umbracoContext.PublishedSnapshot.Domains, _siteDomainMapper, int.Parse(route.Substring(0, pos), CultureInfo.InvariantCulture), current, culture); + + var defaultCulture = _localizationService.GetDefaultLanguageIsoCode(); + if (domainUri is not null || culture == defaultCulture || culture is null) + { + var url = AssembleUrl(domainUri, path, current, mode).ToString(); + return UrlInfo.Url(url, culture); + } + + return null; + } + + #endregion + #region Utilities - Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlMode mode) + private Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlMode mode) { Uri uri; @@ -135,7 +176,9 @@ Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlMode mode) if (domainUri == null) // no domain was found { if (current == null) + { mode = UrlMode.Relative; // best we can do + } switch (mode) { @@ -155,10 +198,15 @@ Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlMode mode) if (mode == UrlMode.Auto) { //this check is a little tricky, we can't just compare domains - if (current != null && domainUri.Uri.GetLeftPart(UriPartial.Authority) == current.GetLeftPart(UriPartial.Authority)) + if (current != null && domainUri.Uri.GetLeftPart(UriPartial.Authority) == + current.GetLeftPart(UriPartial.Authority)) + { mode = UrlMode.Relative; + } else + { mode = UrlMode.Absolute; + } } switch (mode) @@ -179,9 +227,9 @@ Uri AssembleUrl(DomainAndUri domainUri, string path, Uri current, UrlMode mode) return _uriUtility.UriFromUmbraco(uri, _requestSettings); } - string CombinePaths(string path1, string path2) + private string CombinePaths(string path1, string path2) { - string path = path1.TrimEnd(Constants.CharArrays.ForwardSlash) + path2; + var path = path1.TrimEnd(Constants.CharArrays.ForwardSlash) + path2; return path == "/" ? path : path.TrimEnd(Constants.CharArrays.ForwardSlash); } diff --git a/src/Umbraco.Core/Routing/SiteDomainMapper.cs b/src/Umbraco.Core/Routing/SiteDomainMapper.cs index d877385ef107..455889d015eb 100644 --- a/src/Umbraco.Core/Routing/SiteDomainMapper.cs +++ b/src/Umbraco.Core/Routing/SiteDomainMapper.cs @@ -4,18 +4,36 @@ using System.Text.RegularExpressions; using System.Threading; using Umbraco.Extensions; -using System.ComponentModel; namespace Umbraco.Cms.Core.Routing { /// - /// Provides utilities to handle site domains. + /// Provides utilities to handle site domains. /// public class SiteDomainMapper : ISiteDomainMapper, IDisposable { + public void Dispose() => + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(true); + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // This is pretty nasty disposing a static on an instance but it's because this whole class + // is pretty fubar. I'm sure we've fixed this all up in netcore now? We need to remove all statics. + _configLock.Dispose(); + } + + _disposedValue = true; + } + } + #region Configure - private readonly ReaderWriterLockSlim _configLock = new ReaderWriterLockSlim(); + private readonly ReaderWriterLockSlim _configLock = new(); private Dictionary> _qualifiedSites; private bool _disposedValue; @@ -25,10 +43,12 @@ public class SiteDomainMapper : ISiteDomainMapper, IDisposable // these are for validation //private const string DomainValidationSource = @"^(\*|((?i:http[s]?://)?([-\w]+(\.[-\w]+)*)(:\d+)?(/[-\w]*)?))$"; private const string DomainValidationSource = @"^(((?i:http[s]?://)?([-\w]+(\.[-\w]+)*)(:\d+)?(/)?))$"; - private static readonly Regex s_domainValidation = new Regex(DomainValidationSource, RegexOptions.IgnoreCase | RegexOptions.Compiled); + + private static readonly Regex s_domainValidation = + new(DomainValidationSource, RegexOptions.IgnoreCase | RegexOptions.Compiled); /// - /// Clears the entire configuration. + /// Clears the entire configuration. /// public void Clear() { @@ -49,20 +69,21 @@ public void Clear() } } - private IEnumerable ValidateDomains(IEnumerable domains) - { + private IEnumerable ValidateDomains(IEnumerable domains) => // must use authority format w/optional scheme and port, but no path // any domain should appear only once - return domains.Select(domain => + domains.Select(domain => + { + if (!s_domainValidation.IsMatch(domain)) { - if (!s_domainValidation.IsMatch(domain)) - throw new ArgumentOutOfRangeException(nameof(domains), $"Invalid domain: \"{domain}\"."); - return domain; - }); - } + throw new ArgumentOutOfRangeException(nameof(domains), $"Invalid domain: \"{domain}\"."); + } + + return domain; + }); /// - /// Adds a site. + /// Adds a site. /// /// A key uniquely identifying the site. /// The site domains. @@ -87,7 +108,7 @@ public void AddSite(string key, IEnumerable domains) } /// - /// Adds a site. + /// Adds a site. /// /// A key uniquely identifying the site. /// The site domains. @@ -112,7 +133,7 @@ public void AddSite(string key, params string[] domains) } /// - /// Removes a site. + /// Removes a site. /// /// A key uniquely identifying the site. internal void RemoveSite(string key) @@ -122,11 +143,15 @@ internal void RemoveSite(string key) _configLock.EnterWriteLock(); if (Sites == null || !Sites.ContainsKey(key)) + { return; + } Sites.Remove(key); if (Sites.Count == 0) + { Sites = null; + } if (Bindings != null && Bindings.ContainsKey(key)) { @@ -134,11 +159,16 @@ internal void RemoveSite(string key) { Bindings[b].Remove(key); if (Bindings[b].Count == 0) + { Bindings.Remove(b); + } } + Bindings.Remove(key); if (Bindings.Count > 0) + { Bindings = null; + } } _qualifiedSites = null; @@ -153,12 +183,12 @@ internal void RemoveSite(string key) } /// - /// Binds some sites. + /// Binds some sites. /// /// The keys uniquely identifying the sites to bind. /// - /// At the moment there is no public way to unbind sites. Clear and reconfigure. - /// If site1 is bound to site2 and site2 is bound to site3 then site1 is bound to site3. + /// At the moment there is no public way to unbind sites. Clear and reconfigure. + /// If site1 is bound to site2 and site2 is bound to site3 then site1 is bound to site3. /// public void BindSites(params string[] keys) { @@ -167,7 +197,9 @@ public void BindSites(params string[] keys) _configLock.EnterWriteLock(); foreach (var key in keys.Where(key => !Sites.ContainsKey(key))) + { throw new ArgumentException($"Not an existing site key: {key}.", nameof(keys)); + } Bindings = Bindings ?? new Dictionary>(); @@ -180,9 +212,12 @@ public void BindSites(params string[] keys) foreach (var key in allkeys) { if (!Bindings.ContainsKey(key)) + { Bindings[key] = new List(); + } + var xkey = key; - var addKeys = allkeys.Where(k => k != xkey).Except(Bindings[key]); + IEnumerable addKeys = allkeys.Where(k => k != xkey).Except(Bindings[key]); Bindings[key].AddRange(addKeys); } } @@ -200,16 +235,18 @@ public void BindSites(params string[] keys) #region Map domains /// - public virtual DomainAndUri MapDomain(IReadOnlyCollection domainAndUris, Uri current, string culture, string defaultCulture) + public virtual DomainAndUri MapDomain(IReadOnlyCollection domainAndUris, Uri current, + string culture, string defaultCulture) { var currentAuthority = current.GetLeftPart(UriPartial.Authority); - var qualifiedSites = GetQualifiedSites(current); + Dictionary qualifiedSites = GetQualifiedSites(current); return MapDomain(domainAndUris, qualifiedSites, currentAuthority, culture, defaultCulture); } /// - public virtual IEnumerable MapDomains(IReadOnlyCollection domainAndUris, Uri current, bool excludeDefault, string culture, string defaultCulture) + public virtual IEnumerable MapDomains(IReadOnlyCollection domainAndUris, + Uri current, bool excludeDefault, string culture, string defaultCulture) { // TODO: ignoring cultures entirely? @@ -221,15 +258,18 @@ public virtual IEnumerable MapDomains(IReadOnlyCollection qualifiedSites = GetQualifiedSitesInsideLock(current); if (excludeDefault) { // exclude the current one (avoid producing the absolute equivalent of what GetUrl returns) - var hintWithSlash = current.EndPathWithSlash(); - var hinted = domainAndUris.FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(hintWithSlash)); + Uri hintWithSlash = current.EndPathWithSlash(); + DomainAndUri hinted = + domainAndUris.FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(hintWithSlash)); if (hinted != null) + { ret = ret.Where(d => d != hinted); + } // exclude the default one (avoid producing a possible duplicate of what GetUrl returns) // only if the default one cannot be the current one ie if hinted is not null @@ -237,17 +277,21 @@ public virtual IEnumerable MapDomains(IReadOnlyCollection d != mainDomain); } } // we do our best, but can't do the impossible if (qualifiedSites == null) + { return ret; + } // find a site that contains the current authority - var currentSite = qualifiedSites.FirstOrDefault(site => site.Value.Contains(currentAuthority)); + KeyValuePair currentSite = + qualifiedSites.FirstOrDefault(site => site.Value.Contains(currentAuthority)); // if current belongs to a site, pick every element from domainAndUris that also belong // to that site -- or to any site bound to that site @@ -257,7 +301,8 @@ public virtual IEnumerable MapDomains(IReadOnlyCollection Bindings[currentSite.Key].Contains(site.Key)); + IEnumerable> boundSites = + qualifiedSites.Where(site => Bindings[currentSite.Key].Contains(site.Key)); candidateSites = candidateSites.Union(boundSites).ToArray(); // .ToArray ensures it is evaluated before the configuration lock is exited @@ -273,7 +318,9 @@ public virtual IEnumerable MapDomains(IReadOnlyCollection + return candidateSites == null + ? ret + : ret.Where(d => { var authority = d.Uri.GetLeftPart(UriPartial.Authority); return candidateSites.Any(site => site.Value.Contains(authority)); @@ -301,11 +348,15 @@ private Dictionary GetQualifiedSitesInsideLock(Uri current) { // we do our best, but can't do the impossible if (Sites == null) + { return null; + } // cached? if (_qualifiedSites != null && _qualifiedSites.ContainsKey(current.Scheme)) + { return _qualifiedSites[current.Scheme]; + } _qualifiedSites = _qualifiedSites ?? new Dictionary>(); @@ -314,7 +365,10 @@ private Dictionary GetQualifiedSitesInsideLock(Uri current) return _qualifiedSites[current.Scheme] = Sites .ToDictionary( kvp => kvp.Key, - kvp => kvp.Value.Select(d => new Uri(UriUtilityCore.StartWithScheme(d, current.Scheme)).GetLeftPart(UriPartial.Authority)).ToArray() + kvp => kvp.Value.Select(d => + new Uri(UriUtilityCore.StartWithScheme(d, current.Scheme)) + .GetLeftPart(UriPartial.Authority)) + .ToArray() ); // .ToDictionary will evaluate and create the dictionary immediately @@ -322,61 +376,54 @@ private Dictionary GetQualifiedSitesInsideLock(Uri current) // therefore it is safe to return and exit the configuration lock } - private DomainAndUri MapDomain(IReadOnlyCollection domainAndUris, Dictionary qualifiedSites, string currentAuthority, string culture, string defaultCulture) + private DomainAndUri MapDomain(IReadOnlyCollection domainAndUris, + Dictionary qualifiedSites, string currentAuthority, string culture, string defaultCulture) { if (domainAndUris == null) + { throw new ArgumentNullException(nameof(domainAndUris)); + } + if (domainAndUris.Count == 0) + { throw new ArgumentException("Cannot be empty.", nameof(domainAndUris)); + } - // TODO: how shall we deal with cultures? - - // we do our best, but can't do the impossible - // get the "default" domain ie the first one for the culture, else the first one (exists, length > 0) if (qualifiedSites == null) { return domainAndUris.FirstOrDefault(x => x.Culture.InvariantEquals(culture)) - ?? domainAndUris.FirstOrDefault(x => x.Culture.InvariantEquals(defaultCulture)); + ?? domainAndUris.FirstOrDefault(x => x.Culture.InvariantEquals(defaultCulture)) + ?? (culture is null ? domainAndUris.First() : null); } // find a site that contains the current authority - var currentSite = qualifiedSites.FirstOrDefault(site => site.Value.Contains(currentAuthority)); + KeyValuePair currentSite = + qualifiedSites.FirstOrDefault(site => site.Value.Contains(currentAuthority)); // if current belongs to a site - try to pick the first element // from domainAndUris that also belongs to that site - var ret = currentSite.Equals(default(KeyValuePair)) + DomainAndUri ret = currentSite.Equals(default(KeyValuePair)) ? null - : domainAndUris.FirstOrDefault(d => currentSite.Value.Contains(d.Uri.GetLeftPart(UriPartial.Authority))); + : domainAndUris.FirstOrDefault(d => + currentSite.Value.Contains(d.Uri.GetLeftPart(UriPartial.Authority))); // no match means that either current does not belong to a site, or the site it belongs to - // does not contain any of domainAndUris. - return ret; - } + // does not contain any of domainAndUris. Yet we have to return something. here, it becomes + // a bit arbitrary. + // look through sites in order and pick the first domainAndUri that belongs to a site + ret = ret ?? qualifiedSites + .Where(site => site.Key != currentSite.Key) + .Select(site => domainAndUris.FirstOrDefault(domainAndUri => + site.Value.Contains(domainAndUri.Uri.GetLeftPart(UriPartial.Authority)))) + .FirstOrDefault(domainAndUri => domainAndUri != null); - #endregion + // random, really + ret = ret ?? domainAndUris.FirstOrDefault(x => x.Culture.InvariantEquals(culture)) ?? domainAndUris.First(); - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - // This is pretty nasty disposing a static on an instance but it's because this whole class - // is pretty fubar. I'm sure we've fixed this all up in netcore now? We need to remove all statics. - _configLock.Dispose(); - } - - _disposedValue = true; - } - } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); + return ret; } - + #endregion } } diff --git a/src/Umbraco.Core/Routing/UrlProviderExtensions.cs b/src/Umbraco.Core/Routing/UrlProviderExtensions.cs index c3e2bdee77db..3ba254112b22 100644 --- a/src/Umbraco.Core/Routing/UrlProviderExtensions.cs +++ b/src/Umbraco.Core/Routing/UrlProviderExtensions.cs @@ -15,11 +15,11 @@ namespace Umbraco.Extensions public static class UrlProviderExtensions { /// - /// Gets the URLs of the content item. + /// Gets the URLs of the content item. /// /// - /// Use when displaying URLs. If errors occur when generating the URLs, they will show in the list. - /// Contains all the URLs that we can figure out (based upon domains, etc). + /// Use when displaying URLs. If errors occur when generating the URLs, they will show in the list. + /// Contains all the URLs that we can figure out (based upon domains, etc). /// public static async Task> GetContentUrlsAsync( this IContent content, @@ -33,16 +33,55 @@ public static async Task> GetContentUrlsAsync( UriUtility uriUtility, IPublishedUrlProvider publishedUrlProvider) { - if (content == null) throw new ArgumentNullException(nameof(content)); - if (publishedRouter == null) throw new ArgumentNullException(nameof(publishedRouter)); - if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext)); - if (localizationService == null) throw new ArgumentNullException(nameof(localizationService)); - if (textService == null) throw new ArgumentNullException(nameof(textService)); - if (contentService == null) throw new ArgumentNullException(nameof(contentService)); - if (logger == null) throw new ArgumentNullException(nameof(logger)); - if (publishedUrlProvider == null) throw new ArgumentNullException(nameof(publishedUrlProvider)); - if (uriUtility == null) throw new ArgumentNullException(nameof(uriUtility)); - if (variationContextAccessor == null) throw new ArgumentNullException(nameof(variationContextAccessor)); + if (content == null) + { + throw new ArgumentNullException(nameof(content)); + } + + if (publishedRouter == null) + { + throw new ArgumentNullException(nameof(publishedRouter)); + } + + if (umbracoContext == null) + { + throw new ArgumentNullException(nameof(umbracoContext)); + } + + if (localizationService == null) + { + throw new ArgumentNullException(nameof(localizationService)); + } + + if (textService == null) + { + throw new ArgumentNullException(nameof(textService)); + } + + if (contentService == null) + { + throw new ArgumentNullException(nameof(contentService)); + } + + if (logger == null) + { + throw new ArgumentNullException(nameof(logger)); + } + + if (publishedUrlProvider == null) + { + throw new ArgumentNullException(nameof(publishedUrlProvider)); + } + + if (uriUtility == null) + { + throw new ArgumentNullException(nameof(uriUtility)); + } + + if (variationContextAccessor == null) + { + throw new ArgumentNullException(nameof(variationContextAccessor)); + } var result = new List(); @@ -68,7 +107,9 @@ public static async Task> GetContentUrlsAsync( // get all URLs for all cultures // in a HashSet, so de-duplicates too - foreach (UrlInfo cultureUrl in await GetContentUrlsByCultureAsync(content, cultures, publishedRouter, umbracoContext, contentService, textService, variationContextAccessor, logger, uriUtility, publishedUrlProvider)) + foreach (UrlInfo cultureUrl in await GetContentUrlsByCultureAsync(content, cultures, publishedRouter, + umbracoContext, contentService, textService, variationContextAccessor, logger, uriUtility, + publishedUrlProvider)) { urls.Add(cultureUrl); } @@ -79,15 +120,25 @@ public static async Task> GetContentUrlsAsync( // in some cases there will be the same URL for multiple cultures: // * The entire branch is invariant // * If there are less domain/cultures assigned to the branch than the number of cultures/languages installed - foreach (UrlInfo dUrl in urlGroup.DistinctBy(x => x.Text.ToUpperInvariant()).OrderBy(x => x.Text).ThenBy(x => x.Culture)) + + if (urlGroup.Key) + { + result.AddRange(urlGroup.DistinctBy(x => x.Text.ToUpperInvariant()) + .OrderBy(x => x.Text).ThenBy(x => x.Culture)); + } + else { - result.Add(dUrl); + result.AddRange(urlGroup); } + + + } // get the 'other' URLs - ie not what you'd get with GetUrl() but URLs that would route to the document, nevertheless. // for these 'other' URLs, we don't check whether they are routable, collide, anything - we just report them. - foreach (var otherUrl in publishedUrlProvider.GetOtherUrls(content.Id).OrderBy(x => x.Text).ThenBy(x => x.Culture)) + foreach (UrlInfo otherUrl in publishedUrlProvider.GetOtherUrls(content.Id).OrderBy(x => x.Text) + .ThenBy(x => x.Culture)) { // avoid duplicates if (urls.Add(otherUrl)) @@ -100,7 +151,7 @@ public static async Task> GetContentUrlsAsync( } /// - /// Tries to return a for each culture for the content while detecting collisions/errors + /// Tries to return a for each culture for the content while detecting collisions/errors /// private static async Task> GetContentUrlsByCultureAsync( IContent content, @@ -151,7 +202,8 @@ private static async Task> GetContentUrlsByCultureAsync( // got a URL, deal with collisions, add URL default: // detect collisions, etc - Attempt hasCollision = await DetectCollisionAsync(logger, content, url, culture, umbracoContext, publishedRouter, textService, variationContextAccessor, uriUtility); + Attempt hasCollision = await DetectCollisionAsync(logger, content, url, culture, + umbracoContext, publishedRouter, textService, variationContextAccessor, uriUtility); if (hasCollision) { result.Add(hasCollision.Result); @@ -168,7 +220,8 @@ private static async Task> GetContentUrlsByCultureAsync( return result; } - private static UrlInfo HandleCouldNotGetUrl(IContent content, string culture, IContentService contentService, ILocalizedTextService textService) + private static UrlInfo HandleCouldNotGetUrl(IContent content, string culture, IContentService contentService, + ILocalizedTextService textService) { // document has a published version yet its URL is "#" => a parent must be // unpublished, walk up the tree until we find it, and report. @@ -176,27 +229,31 @@ private static UrlInfo HandleCouldNotGetUrl(IContent content, string culture, IC do { parent = parent.ParentId > 0 ? contentService.GetParent(parent) : null; - } - while (parent != null && parent.Published && (!parent.ContentType.VariesByCulture() || parent.IsCulturePublished(culture))); + } while (parent != null && parent.Published && + (!parent.ContentType.VariesByCulture() || parent.IsCulturePublished(culture))); if (parent == null) { // oops, internal error return UrlInfo.Message(textService.Localize("content", "parentNotPublishedAnomaly"), culture); } - else if (!parent.Published) + + if (!parent.Published) { // totally not published - return UrlInfo.Message(textService.Localize("content", "parentNotPublished", new[] { parent.Name }), culture); - } - else - { - // culture not published - return UrlInfo.Message(textService.Localize("content", "parentCultureNotPublished", new[] {parent.Name}), culture); + return UrlInfo.Message(textService.Localize("content", "parentNotPublished", new[] { parent.Name }), + culture); } + + // culture not published + return UrlInfo.Message(textService.Localize("content", "parentCultureNotPublished", new[] { parent.Name }), + culture); } - private static async Task> DetectCollisionAsync(ILogger logger, IContent content, string url, string culture, IUmbracoContext umbracoContext, IPublishedRouter publishedRouter, ILocalizedTextService textService, IVariationContextAccessor variationContextAccessor, UriUtility uriUtility) + private static async Task> DetectCollisionAsync(ILogger logger, IContent content, string url, + string culture, IUmbracoContext umbracoContext, IPublishedRouter publishedRouter, + ILocalizedTextService textService, IVariationContextAccessor variationContextAccessor, + UriUtility uriUtility) { // test for collisions on the 'main' URL var uri = new Uri(url.TrimEnd(Constants.CharArrays.ForwardSlash), UriKind.RelativeOrAbsolute); @@ -207,11 +264,13 @@ private static async Task> DetectCollisionAsync(ILogger logger, uri = uriUtility.UriToUmbraco(uri); IPublishedRequestBuilder builder = await publishedRouter.CreateRequestAsync(uri); - IPublishedRequest pcr = await publishedRouter.RouteRequestAsync(builder, new RouteRequestOptions(RouteDirection.Outbound)); + IPublishedRequest pcr = + await publishedRouter.RouteRequestAsync(builder, new RouteRequestOptions(RouteDirection.Outbound)); if (!pcr.HasPublishedContent()) { - var logMsg = nameof(DetectCollisionAsync) + " did not resolve a content item for original url: {Url}, translated to {TranslatedUrl} and culture: {Culture}"; + var logMsg = nameof(DetectCollisionAsync) + + " did not resolve a content item for original url: {Url}, translated to {TranslatedUrl} and culture: {Culture}"; if (pcr.IgnorePublishedContentCollisions) { logger.LogDebug(logMsg, url, uri, culture); @@ -243,14 +302,7 @@ private static async Task> DetectCollisionAsync(ILogger logger, l.Reverse(); var s = "/" + string.Join("/", l) + " (id=" + pcr.PublishedContent.Id + ")"; - var urlInfo = UrlInfo.Message(textService.Localize("content", "routeError", new[] { s }), culture); - return Attempt.Succeed(urlInfo); - } - - // collisions with a different culture of the same content can never be routed. - if (!culture.InvariantEquals(pcr.Culture)) - { - var urlInfo = UrlInfo.Message(textService.Localize("content", "routeErrorCannotRoute"), culture); + var urlInfo = UrlInfo.Message(textService.Localize("content", "routeError", new[] { s }), culture); return Attempt.Succeed(urlInfo); } diff --git a/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs b/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs index 14627db05c6c..4da1ae53c247 100644 --- a/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs +++ b/src/Umbraco.Infrastructure/PublishedCache/PublishedContentTypeCache.cs @@ -25,6 +25,7 @@ public class PublishedContentTypeCache : IDisposable private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; private readonly ILogger _logger; private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); + private bool _disposedValue; // default ctor public PublishedContentTypeCache(IContentTypeService contentTypeService, IMediaTypeService mediaTypeService, IMemberTypeService memberTypeService, IPublishedContentTypeFactory publishedContentTypeFactory, ILogger logger) @@ -259,8 +260,6 @@ private IPublishedContentType CreatePublishedContentType(PublishedItemType itemT private IPublishedContentType CreatePublishedContentType(PublishedItemType itemType, string alias) { - if (GetPublishedContentTypeByAlias != null) - return GetPublishedContentTypeByAlias(alias); IContentTypeComposition contentType = itemType switch { PublishedItemType.Content => _contentTypeService.Get(alias), @@ -276,8 +275,6 @@ private IPublishedContentType CreatePublishedContentType(PublishedItemType itemT private IPublishedContentType CreatePublishedContentType(PublishedItemType itemType, int id) { - if (GetPublishedContentTypeById != null) - return GetPublishedContentTypeById(id); IContentTypeComposition contentType = itemType switch { PublishedItemType.Content => _contentTypeService.Get(id), @@ -291,56 +288,6 @@ private IPublishedContentType CreatePublishedContentType(PublishedItemType itemT return _publishedContentTypeFactory.CreateContentType(contentType); } - // for unit tests - changing the callback must reset the cache obviously - // TODO: Why does this even exist? For testing you'd pass in a mocked service to get by id - private Func _getPublishedContentTypeByAlias; - internal Func GetPublishedContentTypeByAlias - { - get => _getPublishedContentTypeByAlias; - set - { - try - { - _lock.EnterWriteLock(); - - _typesByAlias.Clear(); - _typesById.Clear(); - _getPublishedContentTypeByAlias = value; - } - finally - { - if (_lock.IsWriteLockHeld) - _lock.ExitWriteLock(); - } - } - } - - // for unit tests - changing the callback must reset the cache obviously - // TODO: Why does this even exist? For testing you'd pass in a mocked service to get by id - private Func _getPublishedContentTypeById; - private bool _disposedValue; - - internal Func GetPublishedContentTypeById - { - get => _getPublishedContentTypeById; - set - { - try - { - _lock.EnterWriteLock(); - - _typesByAlias.Clear(); - _typesById.Clear(); - _getPublishedContentTypeById = value; - } - finally - { - if (_lock.IsWriteLockHeld) - _lock.ExitWriteLock(); - } - } - } - private static string GetAliasKey(PublishedItemType itemType, string alias) { string k; diff --git a/src/Umbraco.PublishedCache.NuCache/ContentCache.cs b/src/Umbraco.PublishedCache.NuCache/ContentCache.cs index 875e6d2ffc05..455c31d00fa5 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentCache.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentCache.cs @@ -13,19 +13,24 @@ using Umbraco.Cms.Core.Xml.XPath; using Umbraco.Cms.Infrastructure.PublishedCache.Navigable; using Umbraco.Extensions; -using Constants = Umbraco.Cms.Core.Constants; namespace Umbraco.Cms.Infrastructure.PublishedCache { public class ContentCache : PublishedCacheBase, IPublishedContentCache, INavigableData, IDisposable { - private readonly ContentStore.Snapshot _snapshot; - private readonly IAppCache _snapshotCache; - private readonly IAppCache _elementsCache; private readonly IDomainCache _domainCache; + private readonly IAppCache _elementsCache; private readonly GlobalSettings _globalSettings; + private readonly ContentStore.Snapshot _snapshot; + private readonly IAppCache _snapshotCache; private readonly IVariationContextAccessor _variationContextAccessor; + #region IDisposable + + public void Dispose() => _snapshot.Dispose(); + + #endregion + #region Constructor // TODO: figure this out @@ -33,7 +38,9 @@ public class ContentCache : PublishedCacheBase, IPublishedContentCache, INavigab // it's too late for UmbracoContext which has captured previewDefault and stuff into these ctor vars // but, no, UmbracoContext returns snapshot.Content which comes from elements SO a resync should create a new cache - public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, IAppCache elementsCache, IDomainCache domainCache, IOptions globalSettings, IVariationContextAccessor variationContextAccessor) + public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCache snapshotCache, + IAppCache elementsCache, IDomainCache domainCache, IOptions globalSettings, + IVariationContextAccessor variationContextAccessor) : base(previewDefault) { _snapshot = snapshot; @@ -59,18 +66,23 @@ public ContentCache(bool previewDefault, ContentStore.Snapshot snapshot, IAppCac // at the moment we try our best to be backward compatible, but really, // should get rid of hideTopLevelNode and other oddities entirely, eventually - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null) - { - return GetByRoute(PreviewDefault, route, hideTopLevelNode, culture); - } + public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null) => + GetByRoute(PreviewDefault, route, hideTopLevelNode, culture); - public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null) + public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, + string culture = null) { - if (route == null) throw new ArgumentNullException(nameof(route)); + if (route == null) + { + throw new ArgumentNullException(nameof(route)); + } - var cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing ? _elementsCache : _snapshotCache; + + IAppCache cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing + ? _elementsCache + : _snapshotCache; var key = CacheKeys.ContentCacheContentByRoute(route, preview, culture); - return cache.GetCacheItem(key, () => GetByRouteInternal(preview, route, hideTopLevelNode, culture)); + return cache.GetCacheItem(key, () => GetByRouteInternal(preview, route, hideTopLevelNode, culture)); } private IPublishedContent GetByRouteInternal(bool preview, string route, bool? hideTopLevelNode, string culture) @@ -108,8 +120,10 @@ private IPublishedContent GetByRouteInternal(bool preview, string route, bool? h // hideTopLevelNode = support legacy stuff, look for /*/path/to/node // else normal, look for /path/to/node content = hideTopLevelNode.Value - ? GetAtRoot(preview).SelectMany(x => x.Children(_variationContextAccessor, culture)).FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]) - : GetAtRoot(preview).FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]); + ? GetAtRoot(preview).SelectMany(x => x.Children(_variationContextAccessor, culture)) + .FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]) + : GetAtRoot(preview) + .FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]); content = FollowRoute(content, parts, 1, culture); } @@ -118,59 +132,72 @@ private IPublishedContent GetByRouteInternal(bool preview, string route, bool? h // have to look for /foo (see note in ApplyHideTopLevelNodeFromPath). if (content == null && hideTopLevelNode.Value && parts.Length == 1) { - content = GetAtRoot(preview).FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]); + content = GetAtRoot(preview) + .FirstOrDefault(x => x.UrlSegment(_variationContextAccessor, culture) == parts[0]); } return content; } - public string GetRouteById(int contentId, string culture = null) - { - return GetRouteById(PreviewDefault, contentId, culture); - } + public string GetRouteById(int contentId, string culture = null) => + GetRouteById(PreviewDefault, contentId, culture); public string GetRouteById(bool preview, int contentId, string culture = null) { - var cache = (preview == false || PublishedSnapshotService.FullCacheWhenPreviewing) ? _elementsCache : _snapshotCache; + IAppCache cache = preview == false || PublishedSnapshotService.FullCacheWhenPreviewing + ? _elementsCache + : _snapshotCache; var key = CacheKeys.ContentCacheRouteByContent(contentId, preview, culture); - return cache.GetCacheItem(key, () => GetRouteByIdInternal(preview, contentId, null, culture)); + return cache.GetCacheItem(key, () => GetRouteByIdInternal(preview, contentId, null, culture)); } private string GetRouteByIdInternal(bool preview, int contentId, bool? hideTopLevelNode, string culture) { - var node = GetById(preview, contentId); + IPublishedContent node = GetById(preview, contentId); if (node == null) + { return null; + } hideTopLevelNode = hideTopLevelNode ?? HideTopLevelNodeFromPath; // default = settings // walk up from that node until we hit a node with a domain, // or we reach the content root, collecting URLs in the way var pathParts = new List(); - var n = node; + IPublishedContent n = node; var urlSegment = n.UrlSegment(_variationContextAccessor, culture); - var hasDomains = _domainCache.HasAssigned(n.Id); + var hasDomains = _domainCache.GetAssignedWithCulture(culture, n.Id); while (hasDomains == false && n != null) // n is null at root { // no segment indicates this is not published when this is a variant - if (urlSegment.IsNullOrWhiteSpace()) return null; + if (urlSegment.IsNullOrWhiteSpace()) + { + return null; + } pathParts.Add(urlSegment); // move to parent node n = n.Parent; if (n != null) + { urlSegment = n.UrlSegment(_variationContextAccessor, culture); + } - hasDomains = n != null && _domainCache.HasAssigned(n.Id); + hasDomains = n != null && _domainCache.GetAssignedWithCulture(culture, n.Id); } // at this point this will be the urlSegment of the root, no segment indicates this is not published when this is a variant - if (urlSegment.IsNullOrWhiteSpace()) return null; + if (urlSegment.IsNullOrWhiteSpace()) + { + return null; + } // no domain, respect HideTopLevelNodeFromPath for legacy purposes if (hasDomains == false && hideTopLevelNode.Value) + { ApplyHideTopLevelNodeFromPath(node, pathParts, preview); + } // assemble the route pathParts.Reverse(); @@ -182,7 +209,8 @@ private string GetRouteByIdInternal(bool preview, int contentId, bool? hideTopLe return route; } - private IPublishedContent FollowRoute(IPublishedContent content, IReadOnlyList parts, int start, string culture) + private IPublishedContent FollowRoute(IPublishedContent content, IReadOnlyList parts, int start, + string culture) { var i = start; while (content != null && i < parts.Count) @@ -194,6 +222,7 @@ private IPublishedContent FollowRoute(IPublishedContent content, IReadOnlyList GetAtRoot(bool preview, string cu { // handle context culture for variant if (culture == null) + { culture = _variationContextAccessor?.VariationContext?.Culture ?? ""; + } // _snapshot.GetAtRoot() returns all ContentNode at root // both .Draft and .Published cannot be null at the same time @@ -272,13 +316,15 @@ public override IEnumerable GetAtRoot(bool preview, string cu // GetNodePublishedContent may return null if !preview and there is no // published model, so we need to filter these nulls out - var atRoot = _snapshot.GetAtRoot() + IEnumerable atRoot = _snapshot.GetAtRoot() .Select(n => GetNodePublishedContent(n, preview)) .WhereNotNull(); // if a culture is specified, we must ensure that it is avail/published if (culture != "*") + { atRoot = atRoot.Where(x => x.IsInvariantOrHasCulture(culture)); + } return atRoot; } @@ -286,7 +332,9 @@ public override IEnumerable GetAtRoot(bool preview, string cu private static IPublishedContent GetNodePublishedContent(ContentNode node, bool preview) { if (node == null) + { return null; + } // both .Draft and .Published cannot be null at the same time @@ -299,7 +347,10 @@ private static IPublishedContent GetNodePublishedContent(ContentNode node, bool // this is for published content when previewing private static IPublishedContent GetPublishedContentAsDraft(IPublishedContent content /*, bool preview*/) { - if (content == null /*|| preview == false*/) return null; //content; + if (content == null /*|| preview == false*/) + { + return null; //content; + } // an object in the cache is either an IPublishedContentOrMedia, // or a model inheriting from PublishedContentExtended - in which @@ -309,12 +360,10 @@ private static IPublishedContent GetPublishedContentAsDraft(IPublishedContent co return inner.AsDraft(); } - public override bool HasContent(bool preview) - { - return preview + public override bool HasContent(bool preview) => + preview ? _snapshot.IsEmpty == false : _snapshot.GetAtRoot().Any(x => x.PublishedModel != null); - } #endregion @@ -322,21 +371,24 @@ public override bool HasContent(bool preview) public override IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars) { - var navigator = CreateNavigator(preview); - var iterator = navigator.Select(xpath, vars); + XPathNavigator navigator = CreateNavigator(preview); + XPathNodeIterator iterator = navigator.Select(xpath, vars); return GetSingleByXPath(iterator); } public override IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) { - var navigator = CreateNavigator(preview); - var iterator = navigator.Select(xpath, vars); + XPathNavigator navigator = CreateNavigator(preview); + XPathNodeIterator iterator = navigator.Select(xpath, vars); return GetSingleByXPath(iterator); } private static IPublishedContent GetSingleByXPath(XPathNodeIterator iterator) { - if (iterator.MoveNext() == false) return null; + if (iterator.MoveNext() == false) + { + return null; + } var xnav = iterator.Current as NavigableNavigator; var xcontent = xnav?.UnderlyingObject as NavigableContent; @@ -345,15 +397,16 @@ private static IPublishedContent GetSingleByXPath(XPathNodeIterator iterator) public override IEnumerable GetByXPath(bool preview, string xpath, XPathVariable[] vars) { - var navigator = CreateNavigator(preview); - var iterator = navigator.Select(xpath, vars); + XPathNavigator navigator = CreateNavigator(preview); + XPathNodeIterator iterator = navigator.Select(xpath, vars); return GetByXPath(iterator); } - public override IEnumerable GetByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) + public override IEnumerable GetByXPath(bool preview, XPathExpression xpath, + XPathVariable[] vars) { - var navigator = CreateNavigator(preview); - var iterator = navigator.Select(xpath, vars); + XPathNavigator navigator = CreateNavigator(preview); + XPathNodeIterator iterator = navigator.Select(xpath, vars); return GetByXPath(iterator); } @@ -364,7 +417,10 @@ private static IEnumerable GetByXPath(XPathNodeIterator itera { var xnav = iterator.Current as NavigableNavigator; var xcontent = xnav?.UnderlyingObject as NavigableContent; - if (xcontent == null) continue; + if (xcontent == null) + { + continue; + } yield return xcontent.InnerContent; } @@ -395,14 +451,5 @@ public override XPathNavigator CreateNodeNavigator(int id, bool preview) public override IPublishedContentType GetContentType(Guid key) => _snapshot.GetContentType(key); #endregion - - #region IDisposable - - public void Dispose() - { - _snapshot.Dispose(); - } - - #endregion } } diff --git a/src/Umbraco.PublishedCache.NuCache/ContentNode.cs b/src/Umbraco.PublishedCache.NuCache/ContentNode.cs index 23088df3adc6..beb598686163 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentNode.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentNode.cs @@ -175,13 +175,6 @@ private IPublishedContent GetModel(ref IPublishedContent model, ContentData cont public IPublishedContent PublishedModel => GetModel(ref _publishedModel, _publishedData); public ContentNodeKit ToKit() - => new ContentNodeKit - { - Node = this, - ContentTypeId = ContentType.Id, - - DraftData = _draftData, - PublishedData = _publishedData - }; + => new ContentNodeKit(this, ContentType.Id, _draftData, _publishedData); } } diff --git a/src/Umbraco.PublishedCache.NuCache/ContentNodeKit.cs b/src/Umbraco.PublishedCache.NuCache/ContentNodeKit.cs index bb05e1470661..3f230925fe34 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentNodeKit.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentNodeKit.cs @@ -1,23 +1,39 @@ -using Umbraco.Cms.Core.Models.PublishedContent; +using System; +using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; namespace Umbraco.Cms.Infrastructure.PublishedCache { - // what's needed to actually build a content node public struct ContentNodeKit { + [Obsolete("This will be changed to a property in future versions")] public ContentNode Node; + + [Obsolete("This will be changed to a property in future versions")] public int ContentTypeId; + + [Obsolete("This will be changed to a property in future versions")] public ContentData DraftData; + + [Obsolete("This will be changed to a property in future versions")] public ContentData PublishedData; + public ContentNodeKit(ContentNode node, int contentTypeId, ContentData draftData, ContentData publishedData) + { + Node = node; + ContentTypeId = contentTypeId; + DraftData = draftData; + PublishedData = publishedData; + } + + public bool IsEmpty => Node == null; public bool IsNull => ContentTypeId < 0; public static ContentNodeKit Empty { get; } = new ContentNodeKit(); - public static ContentNodeKit Null { get; } = new ContentNodeKit { ContentTypeId = -1 }; + public static ContentNodeKit Null { get; } = new ContentNodeKit(null, -1, null, null); public void Build( IPublishedContentType contentType, @@ -41,12 +57,9 @@ public void Build( } public ContentNodeKit Clone(IPublishedModelFactory publishedModelFactory) - => new ContentNodeKit - { - ContentTypeId = ContentTypeId, - DraftData = DraftData, - PublishedData = PublishedData, - Node = new ContentNode(Node, publishedModelFactory) - }; + => new ContentNodeKit(new ContentNode(Node, publishedModelFactory), ContentTypeId, DraftData, PublishedData); + + public ContentNodeKit Clone(IPublishedModelFactory publishedModelFactory, ContentData draftData, ContentData publishedData) + => new ContentNodeKit(new ContentNode(Node, publishedModelFactory), ContentTypeId, draftData, publishedData); } } diff --git a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs index d83d91c0dbc6..42aec80e51a4 100644 --- a/src/Umbraco.PublishedCache.NuCache/ContentStore.cs +++ b/src/Umbraco.PublishedCache.NuCache/ContentStore.cs @@ -104,7 +104,11 @@ public ContentStore( private class WriteLockInfo { +#pragma warning disable IDE1006 // Naming Styles + + // This is a field that is used for ref operations public bool Taken; +#pragma warning restore IDE1006 // Naming Styles } // a scope contextual that represents a locked writer to the dictionary diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs index 99ce9365fcf6..ad093f229241 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentDataSerializer.cs @@ -13,27 +13,25 @@ public ContentDataSerializer(IDictionaryOfPropertyDataSerializer dictionaryOfPro _dictionaryOfPropertyDataSerializer = dictionaryOfPropertyDataSerializer; if(_dictionaryOfPropertyDataSerializer == null) { - _dictionaryOfPropertyDataSerializer = DefaultPropertiesSerializer; + _dictionaryOfPropertyDataSerializer = s_defaultPropertiesSerializer; } } - private static readonly DictionaryOfPropertyDataSerializer DefaultPropertiesSerializer = new DictionaryOfPropertyDataSerializer(); - private static readonly DictionaryOfCultureVariationSerializer DefaultCultureVariationsSerializer = new DictionaryOfCultureVariationSerializer(); + private static readonly DictionaryOfPropertyDataSerializer s_defaultPropertiesSerializer = new DictionaryOfPropertyDataSerializer(); + private static readonly DictionaryOfCultureVariationSerializer s_defaultCultureVariationsSerializer = new DictionaryOfCultureVariationSerializer(); private readonly IDictionaryOfPropertyDataSerializer _dictionaryOfPropertyDataSerializer; public ContentData ReadFrom(Stream stream) { - return new ContentData - { - Published = PrimitiveSerializer.Boolean.ReadFrom(stream), - Name = PrimitiveSerializer.String.ReadFrom(stream), - UrlSegment = PrimitiveSerializer.String.ReadFrom(stream), - VersionId = PrimitiveSerializer.Int32.ReadFrom(stream), - VersionDate = PrimitiveSerializer.DateTime.ReadFrom(stream), - WriterId = PrimitiveSerializer.Int32.ReadFrom(stream), - TemplateId = PrimitiveSerializer.Int32.ReadFrom(stream), - Properties = _dictionaryOfPropertyDataSerializer.ReadFrom(stream), // TODO: We don't want to allocate empty arrays - CultureInfos = DefaultCultureVariationsSerializer.ReadFrom(stream) // TODO: We don't want to allocate empty arrays - }; + var published = PrimitiveSerializer.Boolean.ReadFrom(stream); + var name = PrimitiveSerializer.String.ReadFrom(stream); + var urlSegment = PrimitiveSerializer.String.ReadFrom(stream); + var versionId = PrimitiveSerializer.Int32.ReadFrom(stream); + var versionDate = PrimitiveSerializer.DateTime.ReadFrom(stream); + var writerId = PrimitiveSerializer.Int32.ReadFrom(stream); + var templateId = PrimitiveSerializer.Int32.ReadFrom(stream); + var properties = _dictionaryOfPropertyDataSerializer.ReadFrom(stream); // TODO: We don't want to allocate empty arrays + var cultureInfos = s_defaultCultureVariationsSerializer.ReadFrom(stream); // TODO: We don't want to allocate empty arrays + return new ContentData(name, urlSegment, versionId, versionDate, writerId, templateId, published, properties, cultureInfos); } public void WriteTo(ContentData value, Stream stream) @@ -49,7 +47,7 @@ public void WriteTo(ContentData value, Stream stream) PrimitiveSerializer.Int32.WriteTo(value.TemplateId.Value, stream); } _dictionaryOfPropertyDataSerializer.WriteTo(value.Properties, stream); - DefaultCultureVariationsSerializer.WriteTo(value.CultureInfos, stream); + s_defaultCultureVariationsSerializer.WriteTo(value.CultureInfos, stream); } } } diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentNodeKitSerializer.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentNodeKitSerializer.cs index db0886ce792a..42da3e601ba0 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentNodeKitSerializer.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/BTree.ContentNodeKitSerializer.cs @@ -1,4 +1,4 @@ -using System.IO; +using System.IO; using CSharpTest.Net.Serialization; namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource @@ -10,19 +10,17 @@ public ContentNodeKitSerializer(ContentDataSerializer contentDataSerializer = nu _contentDataSerializer = contentDataSerializer; if(_contentDataSerializer == null) { - _contentDataSerializer = DefaultDataSerializer; + _contentDataSerializer = s_defaultDataSerializer; } } - static readonly ContentDataSerializer DefaultDataSerializer = new ContentDataSerializer(); + static readonly ContentDataSerializer s_defaultDataSerializer = new ContentDataSerializer(); private readonly ContentDataSerializer _contentDataSerializer; //static readonly ListOfIntSerializer ChildContentIdsSerializer = new ListOfIntSerializer(); public ContentNodeKit ReadFrom(Stream stream) { - var kit = new ContentNodeKit - { - Node = new ContentNode( + var contentNode = new ContentNode( PrimitiveSerializer.Int32.ReadFrom(stream), // id PrimitiveSerializer.Guid.ReadFrom(stream), // uid PrimitiveSerializer.Int32.ReadFrom(stream), // level @@ -31,15 +29,27 @@ public ContentNodeKit ReadFrom(Stream stream) PrimitiveSerializer.Int32.ReadFrom(stream), // parent id PrimitiveSerializer.DateTime.ReadFrom(stream), // date created PrimitiveSerializer.Int32.ReadFrom(stream) // creator id - ), - ContentTypeId = PrimitiveSerializer.Int32.ReadFrom(stream) - }; + ); + + int contentTypeId = PrimitiveSerializer.Int32.ReadFrom(stream); var hasDraft = PrimitiveSerializer.Boolean.ReadFrom(stream); + ContentData draftData = null; + ContentData publishedData = null; if (hasDraft) - kit.DraftData = _contentDataSerializer.ReadFrom(stream); + { + draftData = _contentDataSerializer.ReadFrom(stream); + } var hasPublished = PrimitiveSerializer.Boolean.ReadFrom(stream); if (hasPublished) - kit.PublishedData = _contentDataSerializer.ReadFrom(stream); + { + publishedData = _contentDataSerializer.ReadFrom(stream); + } + var kit = new ContentNodeKit( + contentNode, + contentTypeId, + draftData, + publishedData); + return kit; } diff --git a/src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs index a4fd45546899..a461cda437ee 100644 --- a/src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs +++ b/src/Umbraco.PublishedCache.NuCache/DataSource/ContentData.cs @@ -8,19 +8,38 @@ namespace Umbraco.Cms.Infrastructure.PublishedCache.DataSource /// public class ContentData { - public string Name { get; set; } - public string UrlSegment { get; set; } - public int VersionId { get; set; } - public DateTime VersionDate { get; set; } - public int WriterId { get; set; } - public int? TemplateId { get; set; } - public bool Published { get; set; } + [Obsolete("Use ctor with all params, as the pros should be immutable")] + public ContentData() + { - public IDictionary Properties { get; set; } + } + + public ContentData(string name, string urlSegment, int versionId, DateTime versionDate, int writerId, int? templateId, bool published, IDictionary properties, IReadOnlyDictionary cultureInfos) + { + Name = name ?? throw new ArgumentNullException(nameof(name)); + UrlSegment = urlSegment; + VersionId = versionId; + VersionDate = versionDate; + WriterId = writerId; + TemplateId = templateId; + Published = published; + Properties = properties ?? throw new ArgumentNullException(nameof(properties)); + CultureInfos = cultureInfos; + } + + public string Name { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } + public string UrlSegment { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } + public int VersionId { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } + public DateTime VersionDate { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } + public int WriterId { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } + public int? TemplateId { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } + public bool Published { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } + + public IDictionary Properties { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } /// /// The collection of language Id to name for the content item /// - public IReadOnlyDictionary CultureInfos { get; set; } + public IReadOnlyDictionary CultureInfos { get; [Obsolete("Do not change this, use ctor with params and have this object immutable.")] set; } } } diff --git a/src/Umbraco.PublishedCache.NuCache/DomainCacheExtensions.cs b/src/Umbraco.PublishedCache.NuCache/DomainCacheExtensions.cs new file mode 100644 index 000000000000..61f10917fd3c --- /dev/null +++ b/src/Umbraco.PublishedCache.NuCache/DomainCacheExtensions.cs @@ -0,0 +1,15 @@ +using System.Linq; +using Umbraco.Cms.Core.PublishedCache; + +namespace Umbraco.Cms.Infrastructure.PublishedCache +{ + public static class DomainCacheExtensions + { + public static bool GetAssignedWithCulture(this IDomainCache domainCache, string culture, int documentId, bool includeWildcards = false) + { + var assigned = domainCache.GetAssigned(documentId, includeWildcards); + + return culture is null ? assigned.Any() : assigned.Any(x => x.Culture == culture); + } + } +} diff --git a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs index 2919edb8c345..649bc0eebbe6 100644 --- a/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs +++ b/src/Umbraco.PublishedCache.NuCache/Persistence/NuCacheContentRepository.cs @@ -820,18 +820,16 @@ private ContentNodeKit CreateContentNodeKit(ContentSourceDto dto, IContentCacheD bool published = false; var deserializedContent = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published); - d = new ContentData - { - Name = dto.EditName, - Published = published, - TemplateId = dto.EditTemplateId, - VersionId = dto.VersionId, - VersionDate = dto.EditVersionDate, - WriterId = dto.EditWriterId, - Properties = deserializedContent.PropertyData, // TODO: We don't want to allocate empty arrays - CultureInfos = deserializedContent.CultureData, - UrlSegment = deserializedContent.UrlSegment - }; + d = new ContentData( + dto.EditName, + deserializedContent.UrlSegment, + dto.VersionId, + dto.EditVersionDate, + dto.EditWriterId, + dto.EditTemplateId, + published, + deserializedContent.PropertyData, + deserializedContent.CultureData); } } @@ -851,31 +849,23 @@ private ContentNodeKit CreateContentNodeKit(ContentSourceDto dto, IContentCacheD bool published = true; var deserializedContent = serializer.Deserialize(dto, dto.PubData, dto.PubDataRaw, published); - p = new ContentData - { - Name = dto.PubName, - UrlSegment = deserializedContent.UrlSegment, - Published = published, - TemplateId = dto.PubTemplateId, - VersionId = dto.VersionId, - VersionDate = dto.PubVersionDate, - WriterId = dto.PubWriterId, - Properties = deserializedContent.PropertyData, // TODO: We don't want to allocate empty arrays - CultureInfos = deserializedContent.CultureData - }; + p = new ContentData( + dto.PubName, + deserializedContent.UrlSegment, + dto.VersionId, + dto.PubVersionDate, + dto.PubWriterId, + dto.PubTemplateId, + published, + deserializedContent.PropertyData, + deserializedContent.CultureData); } } var n = new ContentNode(dto.Id, dto.Key, dto.Level, dto.Path, dto.SortOrder, dto.ParentId, dto.CreateDate, dto.CreatorId); - var s = new ContentNodeKit - { - Node = n, - ContentTypeId = dto.ContentTypeId, - DraftData = d, - PublishedData = p - }; + var s = new ContentNodeKit(n, dto.ContentTypeId, d, p); return s; } @@ -888,27 +878,21 @@ private ContentNodeKit CreateMediaNodeKit(ContentSourceDto dto, IContentCacheDat bool published = true; var deserializedMedia = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published); - var p = new ContentData - { - Name = dto.EditName, - Published = published, - TemplateId = -1, - VersionId = dto.VersionId, - VersionDate = dto.EditVersionDate, - WriterId = dto.CreatorId, // what-else? - Properties = deserializedMedia.PropertyData, // TODO: We don't want to allocate empty arrays - CultureInfos = deserializedMedia.CultureData - }; + var p = new ContentData( + dto.EditName, + null, + dto.VersionId, + dto.EditVersionDate, + dto.CreatorId, + -1, + published, + deserializedMedia.PropertyData, + deserializedMedia.CultureData); var n = new ContentNode(dto.Id, dto.Key, dto.Level, dto.Path, dto.SortOrder, dto.ParentId, dto.CreateDate, dto.CreatorId); - var s = new ContentNodeKit - { - Node = n, - ContentTypeId = dto.ContentTypeId, - PublishedData = p - }; + var s = new ContentNodeKit(n, dto.ContentTypeId, null, p); return s; } diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedMember.cs b/src/Umbraco.PublishedCache.NuCache/PublishedMember.cs index 4796f3229534..53cc597cf5b1 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedMember.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedMember.cs @@ -34,15 +34,8 @@ public static IPublishedContent Create( IVariationContextAccessor variationContextAccessor, IPublishedModelFactory publishedModelFactory) { - var d = new ContentData - { - Name = member.Name, - Published = previewing, - TemplateId = -1, - VersionDate = member.UpdateDate, - WriterId = member.CreatorId, // what else? - Properties = GetPropertyValues(contentType, member) - }; + var d = new ContentData(member.Name, null, 0, member.UpdateDate, member.CreatorId, -1, previewing, GetPropertyValues(contentType, member), null); + var n = new ContentNode( member.Id, member.Key, diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 83d2bd5ccbfe..20df72608076 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -46,7 +46,6 @@ internal class PublishedSnapshotService : IPublishedSnapshotService private readonly IPublishedModelFactory _publishedModelFactory; private readonly IDefaultCultureAccessor _defaultCultureAccessor; private readonly IHostingEnvironment _hostingEnvironment; - private readonly IContentCacheDataSerializerFactory _contentCacheDataSerializerFactory; private readonly ContentDataSerializer _contentDataSerializer; private readonly NuCacheSettings _config; @@ -93,7 +92,6 @@ public PublishedSnapshotService( IPublishedModelFactory publishedModelFactory, IHostingEnvironment hostingEnvironment, IOptions config, - IContentCacheDataSerializerFactory contentCacheDataSerializerFactory, ContentDataSerializer contentDataSerializer) { _options = options; @@ -111,7 +109,6 @@ public PublishedSnapshotService( _defaultCultureAccessor = defaultCultureAccessor; _globalSettings = globalSettings.Value; _hostingEnvironment = hostingEnvironment; - _contentCacheDataSerializerFactory = contentCacheDataSerializerFactory; _contentDataSerializer = contentDataSerializer; _config = config.Value; _publishedModelFactory = publishedModelFactory; diff --git a/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs b/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs index 1767386088c0..58a686230028 100644 --- a/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs +++ b/src/Umbraco.Web.BackOffice/Security/ConfigureBackOfficeCookieOptions.cs @@ -36,7 +36,6 @@ public class ConfigureBackOfficeCookieOptions : IConfigureNamedOptions _optionsSnapshot; /// /// Initializes a new instance of the class. diff --git a/tests/Umbraco.Tests.Common/Builders/ContentBuilder.cs b/tests/Umbraco.Tests.Common/Builders/ContentBuilder.cs index 452141135213..cb281523388e 100644 --- a/tests/Umbraco.Tests.Common/Builders/ContentBuilder.cs +++ b/tests/Umbraco.Tests.Common/Builders/ContentBuilder.cs @@ -72,7 +72,7 @@ public ContentBuilder WithParent(IContent parent) public ContentBuilder WithContentType(IContentType contentType) { _contentTypeBuilder = null; - _contentType = contentType; + _contentType = contentType; return this; } @@ -172,6 +172,11 @@ public override Content Build() content.SortOrder = sortOrder; content.Trashed = trashed; + if (contentType.DefaultTemplate?.Id > 0) + { + content.TemplateId = contentType.DefaultTemplate.Id; + } + foreach (KeyValuePair cultureName in _cultureNames) { content.SetCultureName(cultureName.Value, cultureName.Key); diff --git a/tests/Umbraco.Tests.Common/Builders/ContentDataBuilder.cs b/tests/Umbraco.Tests.Common/Builders/ContentDataBuilder.cs new file mode 100644 index 000000000000..596d64dd3c5f --- /dev/null +++ b/tests/Umbraco.Tests.Common/Builders/ContentDataBuilder.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using Umbraco.Extensions; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; +using Umbraco.Cms.Tests.Common.Builders.Extensions; +using Umbraco.Cms.Tests.Common.Builders.Interfaces; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Core.PropertyEditors; +using Moq; +using Umbraco.Cms.Infrastructure.Serialization; +using System.Linq; + +namespace Umbraco.Cms.Tests.Common.Builders +{ + + public class ContentDataBuilder : BuilderBase, IWithNameBuilder + { + private string _name; + private DateTime? _now; + private string _segment; + private int? _versionId; + private int? _writerId; + private int? _templateId; + private bool? _published; + private Dictionary _properties; + private Dictionary _cultureInfos; + + string IWithNameBuilder.Name + { + get => _name; + set => _name = value; + } + + public ContentDataBuilder WithVersionDate(DateTime now) + { + _now = now; + return this; + } + + public ContentDataBuilder WithUrlSegment(string segment) + { + _segment = segment; + return this; + } + + public ContentDataBuilder WithVersionId(int versionId) + { + _versionId = versionId; + return this; + } + + public ContentDataBuilder WithWriterId(int writerId) + { + _writerId = writerId; + return this; + } + + public ContentDataBuilder WithTemplateId(int templateId) + { + _templateId = templateId; + return this; + } + + public ContentDataBuilder WithPublished(bool published) + { + _published = published; + return this; + } + + public ContentDataBuilder WithProperties(Dictionary properties) + { + _properties = properties; + return this; + } + + public ContentDataBuilder WithCultureInfos(Dictionary cultureInfos) + { + _cultureInfos = cultureInfos; + return this; + } + + /// + /// Build and dynamically update an existing content type + /// + /// + /// + /// + /// + /// + /// Will configure the content type with this alias/name if supplied when it's not already set on the content type. + /// + /// + /// + public ContentData Build( + IShortStringHelper shortStringHelper, + Dictionary propertyDataTypes, + TContentType contentType, + string contentTypeAlias = null, + bool autoCreateCultureNames = false) where TContentType : class, IContentTypeComposition + { + if (_name.IsNullOrWhiteSpace()) + { + throw new InvalidOperationException("Cannot build without a name"); + } + _segment ??= _name.ToLower().ReplaceNonAlphanumericChars('-'); + + // create or copy the current culture infos for the content + Dictionary contentCultureInfos = _cultureInfos == null + ? new Dictionary() + : new Dictionary(_cultureInfos); + + contentType.Alias ??= contentTypeAlias; + contentType.Name ??= contentTypeAlias; + contentType.Key = contentType.Key == default ? Guid.NewGuid() : contentType.Key; + contentType.Id = contentType.Id == default ? Math.Abs(contentTypeAlias.GetHashCode()) : contentType.Id; + + if (_properties == null) + { + _properties = new Dictionary(); + } + + foreach (KeyValuePair prop in _properties) + { + //var dataType = new DataType(new VoidEditor("Label", Mock.Of()), new ConfigurationEditorJsonSerializer()) + //{ + // Id = 4 + //}; + + if (!propertyDataTypes.TryGetValue(prop.Key, out IDataType dataType)) + { + dataType = propertyDataTypes.First().Value; + } + + var propertyType = new PropertyType(shortStringHelper, dataType, prop.Key); + + // check each property for culture and set variations accordingly, + // this will also ensure that we have the correct culture name on the content + // set for each culture too. + foreach (PropertyData cultureValue in prop.Value.Where(x => !x.Culture.IsNullOrWhiteSpace())) + { + // set the property type to vary based on the values + propertyType.Variations |= ContentVariation.Culture; + + // if there isn't already a culture, then add one with the default name + if (autoCreateCultureNames && !contentCultureInfos.TryGetValue(cultureValue.Culture, out CultureVariation cultureVariation)) + { + cultureVariation = new CultureVariation + { + Date = DateTime.Now, + IsDraft = true, + Name = _name, + UrlSegment = _segment + }; + contentCultureInfos[cultureValue.Culture] = cultureVariation; + } + } + + // set variations for segments if there is any + if (prop.Value.Any(x => !x.Segment.IsNullOrWhiteSpace())) + { + propertyType.Variations |= ContentVariation.Segment; + contentType.Variations |= ContentVariation.Segment; + } + + if (!contentType.PropertyTypeExists(propertyType.Alias)) + { + contentType.AddPropertyType(propertyType); + } + } + + if (contentCultureInfos.Count > 0) + { + contentType.Variations |= ContentVariation.Culture; + WithCultureInfos(contentCultureInfos); + } + + var result = Build(); + return result; + } + + public override ContentData Build() + { + var now = _now ?? DateTime.Now; + var versionId = _versionId ?? 1; + var writerId = _writerId ?? -1; + var templateId = _templateId ?? 0; + var published = _published ?? true; + var properties = _properties ?? new Dictionary(); + var cultureInfos = _cultureInfos ?? new Dictionary(); + var segment = _segment ?? _name.ToLower().ReplaceNonAlphanumericChars('-'); + + var contentData = new ContentData( + _name, + segment, + versionId, + now, + writerId, + templateId, + published, + properties, + cultureInfos); + + return contentData; + } + + public static ContentData CreateBasic(string name, DateTime? versionDate = null) + => new ContentDataBuilder() + .WithName(name) + .WithVersionDate(versionDate ?? DateTime.Now) + .Build(); + + public static ContentData CreateVariant(string name, Dictionary cultureInfos, DateTime? versionDate = null, bool published = true) + => new ContentDataBuilder() + .WithName(name) + .WithVersionDate(versionDate ?? DateTime.Now) + .WithCultureInfos(cultureInfos) + .WithPublished(published) + .Build(); + } +} diff --git a/tests/Umbraco.Tests.Common/Builders/ContentNodeKitBuilder.cs b/tests/Umbraco.Tests.Common/Builders/ContentNodeKitBuilder.cs new file mode 100644 index 000000000000..f3314389ddfc --- /dev/null +++ b/tests/Umbraco.Tests.Common/Builders/ContentNodeKitBuilder.cs @@ -0,0 +1,96 @@ +using System; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; + +namespace Umbraco.Cms.Tests.Common.Builders +{ + public class ContentNodeKitBuilder : BuilderBase + { + private int _contentTypeId; + private ContentNode _contentNode; + private ContentData _draftData; + private ContentData _publishedData; + + public ContentNodeKitBuilder WithContentNode(ContentNode contentNode) + { + _contentNode = contentNode; + return this; + } + + public ContentNodeKitBuilder WithContentNode(int id, Guid uid, int level, string path, int sortOrder, int parentContentId, DateTime createDate, int creatorId) + { + _contentNode = new ContentNode(id, uid, level, path, sortOrder, parentContentId, createDate, creatorId); + return this; + } + + public ContentNodeKitBuilder WithContentTypeId(int contentTypeId) + { + _contentTypeId = contentTypeId; + return this; + } + + public ContentNodeKitBuilder WithDraftData(ContentData draftData) + { + _draftData = draftData; + return this; + } + + public ContentNodeKitBuilder WithPublishedData(ContentData publishedData) + { + _publishedData = publishedData; + return this; + } + + public override ContentNodeKit Build() + { + var data = new ContentNodeKit(_contentNode, _contentTypeId, _draftData, _publishedData); + return data; + } + + /// + /// Creates a ContentNodeKit + /// + /// + /// + /// + /// + /// + /// Optional. Will get calculated based on the path value if not specified. + /// + /// + /// Optional. Will get calculated based on the path value if not specified. + /// + /// + /// + /// + /// + /// + /// + public static ContentNodeKit CreateWithContent( + int contentTypeId, + int id, + string path, + int? sortOrder = null, + int? level = null, + int? parentContentId = null, + int creatorId = -1, + Guid? uid = null, + DateTime? createDate = null, + ContentData draftData = null, + ContentData publishedData = null) + { + var pathParts = path.Split(','); + if (pathParts.Length >= 2) + { + parentContentId ??= int.Parse(pathParts[^2]); + } + + return new ContentNodeKitBuilder() + .WithContentTypeId(contentTypeId) + .WithContentNode(id, uid ?? Guid.NewGuid(), level ?? pathParts.Length - 1, path, sortOrder ?? 0, parentContentId.Value, createDate ?? DateTime.Now, creatorId) + .WithDraftData(draftData) + .WithPublishedData(publishedData) + .Build(); + } + } +} diff --git a/tests/Umbraco.Tests.Common/Builders/Interfaces/IWithAllowAsRootBuilder.cs b/tests/Umbraco.Tests.Common/Builders/Interfaces/IWithAllowAsRootBuilder.cs new file mode 100644 index 000000000000..09148dc048b0 --- /dev/null +++ b/tests/Umbraco.Tests.Common/Builders/Interfaces/IWithAllowAsRootBuilder.cs @@ -0,0 +1,7 @@ +namespace Umbraco.Cms.Tests.Common.Builders.Interfaces +{ + public interface IWithAllowAsRootBuilder + { + bool? AllowAsRoot { get; set; } + } +} diff --git a/tests/Umbraco.Tests.Common/Builders/PropertyDataBuilder.cs b/tests/Umbraco.Tests.Common/Builders/PropertyDataBuilder.cs new file mode 100644 index 000000000000..04cc32711a57 --- /dev/null +++ b/tests/Umbraco.Tests.Common/Builders/PropertyDataBuilder.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using Umbraco.Extensions; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; +using System.Linq; + +namespace Umbraco.Cms.Tests.Common.Builders +{ + public class PropertyDataBuilder : BuilderBase> + { + private readonly Dictionary> _properties = new(); + + public PropertyDataBuilder WithPropertyData(string alias, PropertyData propertyData) + { + if (!_properties.TryGetValue(alias, out List propertyDataCollection)) + { + propertyDataCollection = new List(); + _properties[alias] = propertyDataCollection; + } + + propertyDataCollection.Add(propertyData); + + return this; + } + + public PropertyDataBuilder WithPropertyData(string alias, object value, string culture = null, string segment = null) + => WithPropertyData(alias, new PropertyData + { + Culture = culture ?? string.Empty, + Segment = segment ?? string.Empty, + Value = value + }); + + public override Dictionary Build() + => _properties.ToDictionary(x => x.Key, x => x.Value.ToArray()); + } +} diff --git a/tests/Umbraco.Tests.Common/Published/PublishedContentXml.cs b/tests/Umbraco.Tests.Common/Published/PublishedContentXml.cs new file mode 100644 index 000000000000..f59445291e97 --- /dev/null +++ b/tests/Umbraco.Tests.Common/Published/PublishedContentXml.cs @@ -0,0 +1,152 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; + +namespace Umbraco.Cms.Tests.Common.Published +{ + public static class PublishedContentXml + { + // The content XML that was used for the old PublishContentCacheTests + public static string PublishContentCacheTestsXml() + => @" + + +]> + + + + + + + + + + + + + + +"; + + // The content XML that was used in the old BaseWebTest class + public static string BaseWebTestXml(int templateId) + => @" + + + + +]> + + + + + 1 + + This is some content]]> + + + + + + + + + + + + + + + + + + +"; + + // The content XML that was used in the old TestWithDatabase class + public static string TestWithDatabaseXml(int templateId) + => @" + + + + +]> + + + + + 1 + + This is some content]]> + + + + + + + + + + + + + + + + +"; + + // The content XML that was used in the old PublishedContentTest class + public static string PublishedContentTestXml(int templateId, Guid node1173Guid) + => @" + + + + +]> + + + + + 1 + + + This is some content]]> + + + + + + + + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + + + + + +"; + } +} diff --git a/tests/Umbraco.Tests.Common/Published/PublishedContentXmlAdapter.cs b/tests/Umbraco.Tests.Common/Published/PublishedContentXmlAdapter.cs new file mode 100644 index 000000000000..d24be5ceb784 --- /dev/null +++ b/tests/Umbraco.Tests.Common/Published/PublishedContentXmlAdapter.cs @@ -0,0 +1,146 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System.Collections.Generic; +using System.Xml.Linq; +using System.Xml.XPath; +using System.Linq; +using Umbraco.Extensions; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Builders; +using System; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; +using Umbraco.Cms.Tests.Common.Builders.Extensions; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Core.PropertyEditors; +using Moq; +using Umbraco.Cms.Infrastructure.Serialization; + +namespace Umbraco.Cms.Tests.Common.Published +{ + /// + /// Converts legacy Umbraco XML structures to NuCache collections + /// to populate a test implementation of + /// + /// + /// This does not support variant data because the XML structure doesn't support variant data. + /// + public static class PublishedContentXmlAdapter + { + /// + /// Generate a collection of based on legacy umbraco XML + /// + /// The legacy umbraco XML + /// + /// Dynamically generates a list of s based on the XML data + /// Dynamically generates a list of for tests + /// + public static IEnumerable GetContentNodeKits( + string xml, + IShortStringHelper shortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes) + { + // use the label data type for all data for these tests except in the case + // where a property is named 'content', in which case use the RTE. + var serializer = new ConfigurationEditorJsonSerializer(); + var labelDataType = new DataType(new VoidEditor("Label", Mock.Of()), serializer) { Id = 3 }; + var rteDataType = new DataType(new VoidEditor("RTE", Mock.Of()), serializer) { Id = 4 }; + dataTypes = new[] { labelDataType, rteDataType }; + + var kitsAndXml = new List<(ContentNodeKit kit, XElement node)>(); + + var xDoc = XDocument.Parse(xml); + IEnumerable nodes = xDoc.XPathSelectElements("//*[@isDoc]"); + foreach (XElement node in nodes) + { + var id = node.AttributeValue("id"); + Guid key = node.AttributeValue("key") ?? id.ToGuid(); + + var propertyElements = node.Elements().Where(x => x.Attribute("id") == null); + var properties = new Dictionary(); + foreach(XElement propertyElement in propertyElements) + { + properties[propertyElement.Name.LocalName] = new[] + { + // TODO: builder? + new PropertyData + { + Culture = string.Empty, + Segment = string.Empty, + Value = propertyElement.Value + } + }; + } + + var contentData = new ContentDataBuilder() + .WithName(node.AttributeValue("nodeName")) + .WithProperties(properties) + .WithPublished(true) + .WithTemplateId(node.AttributeValue("template")) + .WithUrlSegment(node.AttributeValue("urlName")) + .WithVersionDate(node.AttributeValue("updateDate")) + .WithWriterId(node.AttributeValue("writerID")) + .Build(); + + ContentNodeKit kit = ContentNodeKitBuilder.CreateWithContent( + node.AttributeValue("nodeType"), + id, + node.AttributeValue("path"), + node.AttributeValue("sortOrder"), + node.AttributeValue("level"), + node.AttributeValue("parentID"), + node.AttributeValue("creatorID"), + key, + node.AttributeValue("createDate"), + contentData, + contentData); + + kitsAndXml.Add((kit, node)); + } + + // put together the unique content types + var contentTypesIdToType = new Dictionary(); + foreach((ContentNodeKit kit, XElement node) in kitsAndXml) + { + if (!contentTypesIdToType.TryGetValue(kit.ContentTypeId, out ContentType contentType)) + { + contentType = new ContentType(shortStringHelper, -1) + { + Id = kit.ContentTypeId, + Alias = node.Name.LocalName + }; + SetContentTypeProperties(shortStringHelper, labelDataType, rteDataType, kit, contentType); + contentTypesIdToType[kit.ContentTypeId] = contentType; + } + else + { + // we've already created it but might need to add properties + SetContentTypeProperties(shortStringHelper, labelDataType, rteDataType, kit, contentType); + } + } + + contentTypes = contentTypesIdToType.Values.ToArray(); + + return kitsAndXml.Select(x => x.kit); + } + + private static void SetContentTypeProperties(IShortStringHelper shortStringHelper, DataType labelDataType, DataType rteDataType, ContentNodeKit kit, ContentType contentType) + { + foreach (KeyValuePair property in kit.DraftData.Properties) + { + var propertyType = new PropertyType(shortStringHelper, labelDataType, property.Key); + + if (!contentType.PropertyTypeExists(propertyType.Alias)) + { + if (propertyType.Alias == "content") + { + propertyType.DataTypeId = rteDataType.Id; + } + contentType.AddPropertyType(propertyType); + } + } + } + } +} diff --git a/tests/Umbraco.Tests.Common/Published/PublishedSnapshotTestObjects.cs b/tests/Umbraco.Tests.Common/Published/PublishedSnapshotTestObjects.cs index 16eb4adda07b..764f646ad8be 100644 --- a/tests/Umbraco.Tests.Common/Published/PublishedSnapshotTestObjects.cs +++ b/tests/Umbraco.Tests.Common/Published/PublishedSnapshotTestObjects.cs @@ -8,6 +8,7 @@ namespace Umbraco.Cms.Tests.Common.Published { + public class PublishedSnapshotTestObjects { [PublishedModel("element1")] diff --git a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/AutoPublishedContentType.cs b/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/AutoPublishedContentType.cs deleted file mode 100644 index b36f7e90d130..000000000000 --- a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/AutoPublishedContentType.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System; -using System.Collections.Generic; -using System.Linq; -using Moq; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Infrastructure.Serialization; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Tests.Common.TestHelpers.PublishedContent -{ - - public class AutoPublishedContentType : PublishedContentType - { - private static readonly IPublishedPropertyType Default; - - static AutoPublishedContentType() - { - var configurationEditorJsonSerializer = new ConfigurationEditorJsonSerializer(); - var jsonSerializer = new JsonNetSerializer(); - var dataTypeServiceMock = new Mock(); - - var dataType = new DataType( - new VoidEditor( - Mock.Of()), - configurationEditorJsonSerializer) - { - Id = 666 - }; - dataTypeServiceMock.Setup(x => x.GetAll()).Returns(dataType.Yield); - - var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(() => Enumerable.Empty()), dataTypeServiceMock.Object); - Default = factory.CreatePropertyType("*", 666); - } - - public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable propertyTypes) - : base(key, id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) - { - } - - public AutoPublishedContentType(Guid key, int id, string alias, Func> propertyTypes) - : base(key, id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) - { - } - - public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) - : base(key, id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) - { - } - - public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable compositionAliases, Func> propertyTypes) - : base(key, id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) - { - } - - public override IPublishedPropertyType GetPropertyType(string alias) - { - IPublishedPropertyType propertyType = base.GetPropertyType(alias); - return propertyType ?? Default; - } - } -} diff --git a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/ContentType2.cs b/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/ContentType2.cs deleted file mode 100644 index 37d2bb6949ee..000000000000 --- a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/ContentType2.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Moq; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Tests.Common.TestHelpers.PublishedContent -{ - [PublishedModel("ContentType2")] - public class ContentType2 : PublishedContentModel - { - public ContentType2(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - { - } - - public int Prop1 => this.Value(Mock.Of(), "prop1"); - } -} diff --git a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/ContentType2Sub.cs b/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/ContentType2Sub.cs deleted file mode 100644 index 0e2d6f9f168f..000000000000 --- a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/ContentType2Sub.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Umbraco.Cms.Core.Models.PublishedContent; - -namespace Umbraco.Cms.Tests.Common.TestHelpers.PublishedContent -{ - [PublishedModel("ContentType2Sub")] - public class ContentType2Sub : ContentType2 - { - public ContentType2Sub(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - { - } - } -} diff --git a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/InternalPublishedPropertyWithLanguageVariants.cs b/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/InternalPublishedPropertyWithLanguageVariants.cs deleted file mode 100644 index 842cba60619e..000000000000 --- a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/InternalPublishedPropertyWithLanguageVariants.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using System.Collections.Generic; -using Umbraco.Cms.Core.PublishedCache.Internal; - -namespace Umbraco.Cms.Tests.Common.TestHelpers.PublishedContent -{ - public class InternalPublishedPropertyWithLanguageVariants : InternalPublishedProperty - { - private readonly IDictionary _solidSourceValues = new Dictionary(); - private readonly IDictionary _solidValues = new Dictionary(); - private readonly IDictionary _solidXPathValues = new Dictionary(); - - public override object GetSourceValue(string culture = null, string segment = null) - { - if (string.IsNullOrEmpty(culture)) - { - return base.GetSourceValue(culture, segment); - } - - return _solidSourceValues.ContainsKey(culture) ? _solidSourceValues[culture] : null; - } - - public override object GetValue(string culture = null, string segment = null) - { - if (string.IsNullOrEmpty(culture)) - { - return base.GetValue(culture, segment); - } - - return _solidValues.ContainsKey(culture) ? _solidValues[culture] : null; - } - - public override object GetXPathValue(string culture = null, string segment = null) - { - if (string.IsNullOrEmpty(culture)) - { - return base.GetXPathValue(culture, segment); - } - - return _solidXPathValues.ContainsKey(culture) ? _solidXPathValues[culture] : null; - } - - public override bool HasValue(string culture = null, string segment = null) - { - if (string.IsNullOrEmpty(culture)) - { - return base.HasValue(culture, segment); - } - - return _solidSourceValues.ContainsKey(culture); - } - - public void SetSourceValue(string culture, object value, bool defaultValue = false) - { - _solidSourceValues.Add(culture, value); - if (defaultValue) - { - SolidSourceValue = value; - SolidHasValue = true; - } - } - - public void SetValue(string culture, object value, bool defaultValue = false) - { - _solidValues.Add(culture, value); - if (defaultValue) - { - SolidValue = value; - SolidHasValue = true; - } - } - - public void SetXPathValue(string culture, object value, bool defaultValue = false) - { - _solidXPathValues.Add(culture, value); - if (defaultValue) - { - SolidXPathValue = value; - } - } - } -} diff --git a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong1.cs b/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong1.cs deleted file mode 100644 index 8c2d501d3e6f..000000000000 --- a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong1.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Moq; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Tests.Common.TestHelpers.PublishedContent -{ - public class PublishedContentStrong1 : PublishedContentModel - { - public PublishedContentStrong1(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - { - } - - public int StrongValue => this.Value(Mock.Of(), "strongValue"); - } -} diff --git a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong1Sub.cs b/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong1Sub.cs deleted file mode 100644 index 96748b413652..000000000000 --- a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong1Sub.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Moq; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Tests.Common.TestHelpers.PublishedContent -{ - public class PublishedContentStrong1Sub : PublishedContentStrong1 - { - public PublishedContentStrong1Sub(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - { - } - - public int AnotherValue => this.Value(Mock.Of(), "anotherValue"); - } -} diff --git a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong2.cs b/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong2.cs deleted file mode 100644 index 0b88c56eaa27..000000000000 --- a/tests/Umbraco.Tests.Common/TestHelpers/PublishedContent/PublishedContentStrong2.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Umbraco. -// See LICENSE for more details. - -using Moq; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Extensions; - -namespace Umbraco.Cms.Tests.Common.TestHelpers.PublishedContent -{ - public class PublishedContentStrong2 : PublishedContentModel - { - public PublishedContentStrong2(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - { - } - - public int StrongValue => this.Value(Mock.Of(), "strongValue"); - } -} diff --git a/tests/Umbraco.Tests.Common/TestLastChanceFinder.cs b/tests/Umbraco.Tests.Common/TestLastChanceFinder.cs new file mode 100644 index 000000000000..6a3c2e956a82 --- /dev/null +++ b/tests/Umbraco.Tests.Common/TestLastChanceFinder.cs @@ -0,0 +1,12 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using Umbraco.Cms.Core.Routing; + +namespace Umbraco.Cms.Tests.Common +{ + public class TestLastChanceFinder : IContentLastChanceFinder + { + public bool TryFindContent(IPublishedRequestBuilder frequest) => false; + } +} diff --git a/tests/Umbraco.Tests.Common/TestPublishedSnapshotAccessor.cs b/tests/Umbraco.Tests.Common/TestPublishedSnapshotAccessor.cs index 400a79292e48..de6ed7b0b6ba 100644 --- a/tests/Umbraco.Tests.Common/TestPublishedSnapshotAccessor.cs +++ b/tests/Umbraco.Tests.Common/TestPublishedSnapshotAccessor.cs @@ -7,10 +7,14 @@ namespace Umbraco.Cms.Tests.Common { public class TestPublishedSnapshotAccessor : IPublishedSnapshotAccessor { + private IPublishedSnapshot _snapshot = null; + public bool TryGetPublishedSnapshot(out IPublishedSnapshot publishedSnapshot) { - publishedSnapshot = null; - return false; + publishedSnapshot = _snapshot; + return _snapshot != null; } + + public void SetCurrent(IPublishedSnapshot snapshot) => _snapshot = snapshot; } } diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs index 8113c1b3e9e2..946d4a236a8d 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentTypeServiceVariantsTests.cs @@ -52,9 +52,14 @@ protected override void BeforeHostStart(IHost host) protected override void CustomTestSetup(IUmbracoBuilder builder) { - InMemoryConfiguration[Constants.Configuration.ConfigNuCache + ":" + nameof(NuCacheSettings.NuCacheSerializerType)] = NuCacheSerializerType.JSON.ToString(); builder.AddNuCache(); builder.Services.AddUnique(); + builder.Services.PostConfigure(options => + { + options.NuCacheSerializerType = NuCacheSerializerType.JSON; + }); + + } private void AssertJsonStartsWith(int id, string expected) diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityXmlSerializerTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityXmlSerializerTests.cs index 31a4ee2c0dc7..514b600e4815 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityXmlSerializerTests.cs +++ b/tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityXmlSerializerTests.cs @@ -6,7 +6,13 @@ using System.Diagnostics; using System.Linq; using System.Xml.Linq; +using Microsoft.Extensions.Logging.Abstractions; +using Moq; using NUnit.Framework; +using Umbraco.Extensions; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Tests.Common.Builders; @@ -14,6 +20,12 @@ using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; using Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services.Importing; +using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.PropertyEditors; +using Microsoft.Extensions.Options; +using Microsoft.Extensions.DependencyInjection; +using Umbraco.Cms.Core.Media; +using Umbraco.Cms.Core.Strings; namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services { @@ -22,6 +34,14 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services public class EntityXmlSerializerTests : UmbracoIntegrationTest { private IEntityXmlSerializer Serializer => GetRequiredService(); + private IContentService ContentService => GetRequiredService(); + private IMediaService MediaService => GetRequiredService(); + private IUserService UserService => GetRequiredService(); + private IMediaTypeService MediaTypeService => GetRequiredService(); + private IContentTypeService ContentTypeService => GetRequiredService(); + private IDataValueEditorFactory DataValueEditorFactory => GetRequiredService(); + private ILocalizedTextService TextService => GetRequiredService(); + private IFileService FileService => GetRequiredService(); [Test] public void Can_Export_Macro() @@ -89,6 +109,123 @@ public void Can_Export_Languages() Assert.That(xml.ToString(), Is.EqualTo(languageItemsElement.ToString())); } + [Test] + public void Can_Generate_Xml_Representation_Of_Content() + { + // Arrange + var template = TemplateBuilder.CreateTextPageTemplate(); + FileService.SaveTemplate(template); // else, FK violation on contentType! + var contentType = ContentTypeBuilder.CreateTextPageContentType( + defaultTemplateId: template.Id); + ContentTypeService.Save(contentType); + + var content = ContentBuilder.CreateTextpageContent(contentType, "Root Home", -1); + ContentService.Save(content, Constants.Security.SuperUserId); + + var nodeName = content.ContentType.Alias.ToSafeAlias(ShortStringHelper); + var urlName = content.GetUrlSegment(ShortStringHelper, new[] { new DefaultUrlSegmentProvider(ShortStringHelper) }); + + // Act + XElement element = content.ToXml(Serializer); + + // Assert + Assert.That(element, Is.Not.Null); + Assert.That(element.Name.LocalName, Is.EqualTo(nodeName)); + Assert.AreEqual(content.Id.ToString(), (string)element.Attribute("id")); + Assert.AreEqual(content.ParentId.ToString(), (string)element.Attribute("parentID")); + Assert.AreEqual(content.Level.ToString(), (string)element.Attribute("level")); + Assert.AreEqual(content.CreatorId.ToString(), (string)element.Attribute("creatorID")); + Assert.AreEqual(content.SortOrder.ToString(), (string)element.Attribute("sortOrder")); + Assert.AreEqual(content.CreateDate.ToString("s"), (string)element.Attribute("createDate")); + Assert.AreEqual(content.UpdateDate.ToString("s"), (string)element.Attribute("updateDate")); + Assert.AreEqual(content.Name, (string)element.Attribute("nodeName")); + Assert.AreEqual(urlName, (string)element.Attribute("urlName")); + Assert.AreEqual(content.Path, (string)element.Attribute("path")); + Assert.AreEqual("", (string)element.Attribute("isDoc")); + Assert.AreEqual(content.ContentType.Id.ToString(), (string)element.Attribute("nodeType")); + Assert.AreEqual(content.GetCreatorProfile(UserService).Name, (string)element.Attribute("creatorName")); + Assert.AreEqual(content.GetWriterProfile(UserService).Name, (string)element.Attribute("writerName")); + Assert.AreEqual(content.WriterId.ToString(), (string)element.Attribute("writerID")); + Assert.AreEqual(content.TemplateId.ToString(), (string)element.Attribute("template")); + + Assert.AreEqual(content.Properties["title"].GetValue().ToString(), element.Elements("title").Single().Value); + Assert.AreEqual(content.Properties["bodyText"].GetValue().ToString(), element.Elements("bodyText").Single().Value); + Assert.AreEqual(content.Properties["keywords"].GetValue().ToString(), element.Elements("keywords").Single().Value); + Assert.AreEqual(content.Properties["description"].GetValue().ToString(), element.Elements("description").Single().Value); + } + + [Test] + public void Can_Generate_Xml_Representation_Of_Media() + { + // Arrange + var mediaType = MediaTypeBuilder.CreateImageMediaType("image2"); + + MediaTypeService.Save(mediaType); + + // reference, so static ctor runs, so event handlers register + // and then, this will reset the width, height... because the file does not exist, of course ;-( + var loggerFactory = NullLoggerFactory.Instance; + var scheme = Mock.Of(); + var contentSettings = new ContentSettings(); + + var mediaFileManager = new MediaFileManager( + Mock.Of(), + scheme, + loggerFactory.CreateLogger(), + ShortStringHelper, + Services, + Options.Create(new ContentSettings())); + + var ignored = new FileUploadPropertyEditor( + DataValueEditorFactory, + mediaFileManager, + Options.Create(contentSettings), + TextService, + Services.GetRequiredService(), + ContentService, + IOHelper); + + var media = MediaBuilder.CreateMediaImage(mediaType, -1); + media.WriterId = -1; // else it's zero and that's not a user and it breaks the tests + MediaService.Save(media, Constants.Security.SuperUserId); + + // so we have to force-reset these values because the property editor has cleared them + media.SetValue(Constants.Conventions.Media.Width, "200"); + media.SetValue(Constants.Conventions.Media.Height, "200"); + media.SetValue(Constants.Conventions.Media.Bytes, "100"); + media.SetValue(Constants.Conventions.Media.Extension, "png"); + + var nodeName = media.ContentType.Alias.ToSafeAlias(ShortStringHelper); + var urlName = media.GetUrlSegment(ShortStringHelper, new[] { new DefaultUrlSegmentProvider(ShortStringHelper) }); + + // Act + XElement element = media.ToXml(Serializer); + + // Assert + Assert.That(element, Is.Not.Null); + Assert.That(element.Name.LocalName, Is.EqualTo(nodeName)); + Assert.AreEqual(media.Id.ToString(), (string)element.Attribute("id")); + Assert.AreEqual(media.ParentId.ToString(), (string)element.Attribute("parentID")); + Assert.AreEqual(media.Level.ToString(), (string)element.Attribute("level")); + Assert.AreEqual(media.SortOrder.ToString(), (string)element.Attribute("sortOrder")); + Assert.AreEqual(media.CreateDate.ToString("s"), (string)element.Attribute("createDate")); + Assert.AreEqual(media.UpdateDate.ToString("s"), (string)element.Attribute("updateDate")); + Assert.AreEqual(media.Name, (string)element.Attribute("nodeName")); + Assert.AreEqual(urlName, (string)element.Attribute("urlName")); + Assert.AreEqual(media.Path, (string)element.Attribute("path")); + Assert.AreEqual("", (string)element.Attribute("isDoc")); + Assert.AreEqual(media.ContentType.Id.ToString(), (string)element.Attribute("nodeType")); + Assert.AreEqual(media.GetCreatorProfile(UserService).Name, (string)element.Attribute("writerName")); + Assert.AreEqual(media.CreatorId.ToString(), (string)element.Attribute("writerID")); + Assert.IsNull(element.Attribute("template")); + + Assert.AreEqual(media.Properties[Constants.Conventions.Media.File].GetValue().ToString(), element.Elements(Constants.Conventions.Media.File).Single().Value); + Assert.AreEqual(media.Properties[Constants.Conventions.Media.Width].GetValue().ToString(), element.Elements(Constants.Conventions.Media.Width).Single().Value); + Assert.AreEqual(media.Properties[Constants.Conventions.Media.Height].GetValue().ToString(), element.Elements(Constants.Conventions.Media.Height).Single().Value); + Assert.AreEqual(media.Properties[Constants.Conventions.Media.Bytes].GetValue().ToString(), element.Elements(Constants.Conventions.Media.Bytes).Single().Value); + Assert.AreEqual(media.Properties[Constants.Conventions.Media.Extension].GetValue().ToString(), element.Elements(Constants.Conventions.Media.Extension).Single().Value); + } + private void CreateDictionaryData() { ILocalizationService localizationService = GetRequiredService(); @@ -104,7 +241,7 @@ private void CreateDictionaryData() .Build(); localizationService.Save(languageEnGb); - var parentItem = new DictionaryItem("Parent") {Key = Guid.Parse("28f2e02a-8c66-4fcd-85e3-8524d551c0d3")}; + var parentItem = new DictionaryItem("Parent") { Key = Guid.Parse("28f2e02a-8c66-4fcd-85e3-8524d551c0d3") }; var parentTranslations = new List { new DictionaryTranslation(languageNbNo, "ForelderVerdi"), @@ -113,7 +250,7 @@ private void CreateDictionaryData() parentItem.Translations = parentTranslations; localizationService.Save(parentItem); - var childItem = new DictionaryItem(parentItem.Key, "Child"){Key = Guid.Parse("e7dba0a9-d517-4ba4-8e18-2764d392c611")}; + var childItem = new DictionaryItem(parentItem.Key, "Child") { Key = Guid.Parse("e7dba0a9-d517-4ba4-8e18-2764d392c611") }; var childTranslations = new List { new DictionaryTranslation(languageNbNo, "BarnVerdi"), diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj index 8ef12b292527..ca6096835aff 100644 --- a/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj +++ b/tests/Umbraco.Tests.Integration/Umbraco.Tests.Integration.csproj @@ -52,6 +52,8 @@ True ImportResources.resx + + diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/DomainAndUrlsTests.cs b/tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/DomainAndUrlsTests.cs new file mode 100644 index 000000000000..39a5fd2e5f0e --- /dev/null +++ b/tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/DomainAndUrlsTests.cs @@ -0,0 +1,153 @@ +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Packaging; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Tests.Common; +using Umbraco.Cms.Tests.Common.Testing; +using Umbraco.Cms.Tests.Integration.Testing; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Web.BackOffice.UrlAndDomains +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, Mapper = true, WithApplication = true, + Logger = UmbracoTestOptions.Logger.Console)] + public class DomainAndUrlsTests : UmbracoIntegrationTest + { + [SetUp] + public void Setup() + { + XDocument xml = PackageMigrationResource.GetEmbeddedPackageDataManifest(GetType()); + IPackagingService packagingService = GetRequiredService(); + InstallationSummary = packagingService.InstallCompiledPackageData(xml); + + Root = InstallationSummary.ContentInstalled.First(); + ContentService.SaveAndPublish(Root); + + var cultures = new List(); + cultures.Add(GetRequiredService().GetDefaultLanguageIsoCode()); + + foreach (ILanguage language in InstallationSummary.LanguagesInstalled) + { + cultures.Add(language.IsoCode); + } + + Cultures = cultures.ToArray(); + + IHttpContextAccessor httpContextAccessor = GetRequiredService(); + + httpContextAccessor.HttpContext = new DefaultHttpContext + { + Request = + { + Scheme = "https", + Host = new HostString("localhost"), + Path = "/", + QueryString = new QueryString(string.Empty) + } + }; + + //Like the request middleware we specify the VariationContext to the default language. + _variationContextAccessor.VariationContext = new VariationContext(Cultures[0]); + GetRequiredService().EnsureUmbracoContext(); + } + + private IContentService ContentService => GetRequiredService(); + + public InstallationSummary InstallationSummary { get; set; } + + protected override void CustomTestSetup(IUmbracoBuilder builder) + { + builder.Services.AddUnique(_variationContextAccessor); + builder.AddNuCache(); + } + + private readonly TestVariationContextAccessor _variationContextAccessor = new TestVariationContextAccessor(); + + public IContent Root { get; set; } + public string[] Cultures { get; set; } + + + [Test] + public void Having_three_cultures_and_set_domain_on_all_of_them() + { + foreach (var culture in Cultures) + { + SetDomainOnContent(Root, culture, GetDomainUrlFromCultureCode(culture)); + } + + IEnumerable rootUrls = GetContentUrlsAsync(Root); + + Assert.Multiple(() => + { + Assert.AreEqual(6, rootUrls.Count()); + foreach (var culture in Cultures) + { + var domain = GetDomainUrlFromCultureCode(culture); + Assert.IsTrue(rootUrls.Any(x => x.Text == domain)); + Assert.IsTrue(rootUrls.Any(x => x.Text == "https://localhost" + domain)); + } + }); + } + + [Test] + public void Having_three_cultures_but_set_domain_on_a_non_default_language() + { + var culture = Cultures[1]; + var domain = GetDomainUrlFromCultureCode(culture); + SetDomainOnContent(Root, culture, domain); + + IEnumerable rootUrls = GetContentUrlsAsync(Root); + + Assert.Multiple(() => + { + Assert.AreEqual(4, rootUrls.Count()); + + //We expect two for the domain that is setup + Assert.IsTrue(rootUrls.Any(x => x.IsUrl && x.Text == domain && x.Culture == culture)); + Assert.IsTrue(rootUrls.Any(x => x.IsUrl && x.Text == "https://localhost" + domain && x.Culture == culture)); + + //We expect the default language to be routable on the default path "/" + Assert.IsTrue(rootUrls.Any(x=>x.IsUrl && x.Text == "/" && x.Culture == Cultures[0])); + + //We dont expect non-default languages without a domain to be routable + Assert.IsTrue(rootUrls.Any(x=>x.IsUrl == false && x.Culture == Cultures[2])); + }); + } + + private static string GetDomainUrlFromCultureCode(string culture) => + "/" + culture.Replace("-", string.Empty).ToLower() + "/"; + + private void SetDomainOnContent(IContent content, string cultureIsoCode, string domain) + { + IDomainService domainService = GetRequiredService(); + var langId = GetRequiredService().GetLanguageIdByIsoCode(cultureIsoCode); + domainService.Save( + new UmbracoDomain(domain) { RootContentId = content.Id, LanguageId = langId }); + } + + private IEnumerable GetContentUrlsAsync(IContent root) => + root.GetContentUrlsAsync( + GetRequiredService(), + GetRequiredService().GetRequiredUmbracoContext(), + GetRequiredService(), + GetRequiredService(), + ContentService, + GetRequiredService(), + GetRequiredService>(), + GetRequiredService(), + GetRequiredService() + ).GetAwaiter().GetResult(); + + } +} diff --git a/tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/package.xml b/tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/package.xml new file mode 100644 index 000000000000..d07047ecfa2d --- /dev/null +++ b/tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/package.xml @@ -0,0 +1,77 @@ + + + + + Test + + + + + + + + + + + TextPageLang + textPageLang + d4a44303-bf68-4f34-9f87-6668ff9e9cdc + icon-document + folder.png + + True + False + False + Culture + + + + + TextPageLang + + + textPage + textPageLang + + + + Grid + grid + 74dcad47-03e6-4190-a7d5-480bd1d96783 + Umbraco.Grid + 9ccf22c3-34c5-4e58-b365-a2d806c00540 + Content + 0 + False + False + Culture + + + + + 2074 + 1cd3a297-a821-49e9-98d8-5c21ddd2aeaf + Group + Content + content + 0 + + + + + + + + + + + + + + + + + + + + diff --git a/tests/Umbraco.Tests.UnitTests/TestHelpers/PublishedSnapshotServiceTestBase.cs b/tests/Umbraco.Tests.UnitTests/TestHelpers/PublishedSnapshotServiceTestBase.cs new file mode 100644 index 000000000000..87056eb6873e --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/TestHelpers/PublishedSnapshotServiceTestBase.cs @@ -0,0 +1,278 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.PropertyEditors.ValueConverters; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Core.Sync; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; +using Umbraco.Cms.Infrastructure.Serialization; +using Umbraco.Cms.Tests.Common; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.TestHelpers +{ + [TestFixture] + public class PublishedSnapshotServiceTestBase + { + [SetUp] + public virtual void Setup() + { + VariationContextAccessor = new TestVariationContextAccessor(); + PublishedSnapshotAccessor = new TestPublishedSnapshotAccessor(); + } + + [TearDown] + public void Teardown() => SnapshotService?.Dispose(); + + protected IShortStringHelper ShortStringHelper { get; } = TestHelper.ShortStringHelper; + protected virtual IPublishedModelFactory PublishedModelFactory { get; } = new NoopPublishedModelFactory(); + protected IContentTypeService ContentTypeService { get; private set; } + protected IMediaTypeService MediaTypeService { get; private set; } + protected IDataTypeService DataTypeService { get; private set; } + protected IDomainService DomainService { get; private set; } + protected IPublishedValueFallback PublishedValueFallback { get; private set; } + protected IPublishedSnapshotService SnapshotService { get; private set; } + protected IVariationContextAccessor VariationContextAccessor { get; private set; } + protected TestPublishedSnapshotAccessor PublishedSnapshotAccessor { get; private set; } + protected TestNuCacheContentService NuCacheContentService { get; private set; } + protected PublishedContentTypeFactory PublishedContentTypeFactory { get; private set; } + protected GlobalSettings GlobalSettings { get; } = new(); + + protected virtual PropertyValueConverterCollection PropertyValueConverterCollection => + new(() => new[] { new TestSimpleTinyMceValueConverter() }); + + protected IPublishedContent GetContent(int id) + { + IPublishedSnapshot snapshot = GetPublishedSnapshot(); + IPublishedContent doc = snapshot.Content.GetById(id); + Assert.IsNotNull(doc); + return doc; + } + + protected IPublishedContent GetMedia(int id) + { + IPublishedSnapshot snapshot = GetPublishedSnapshot(); + IPublishedContent doc = snapshot.Media.GetById(id); + Assert.IsNotNull(doc); + return doc; + } + + protected UrlProvider GetUrlProvider( + IUmbracoContextAccessor umbracoContextAccessor, + RequestHandlerSettings requestHandlerSettings, + WebRoutingSettings webRoutingSettings, + out UriUtility uriUtility) + { + uriUtility = new UriUtility(Mock.Of()); + var urlProvider = new DefaultUrlProvider( + Options.Create(requestHandlerSettings), + Mock.Of>(), + new SiteDomainMapper(), + umbracoContextAccessor, + uriUtility, + Mock.Of(x=>x.GetDefaultLanguageIsoCode() == GlobalSettings.DefaultUILanguage) + ); + + var publishedUrlProvider = new UrlProvider( + umbracoContextAccessor, + Options.Create(webRoutingSettings), + new UrlProviderCollection(() => new[] { urlProvider }), + new MediaUrlProviderCollection(() => Enumerable.Empty()), + Mock.Of()); + + return publishedUrlProvider; + } + + protected static PublishedRouter CreatePublishedRouter( + IUmbracoContextAccessor umbracoContextAccessor, + IEnumerable contentFinders = null, + IPublishedUrlProvider publishedUrlProvider = null) => new(Options.Create(new WebRoutingSettings()), + new ContentFinderCollection(() => contentFinders ?? Enumerable.Empty()), + new TestLastChanceFinder(), new TestVariationContextAccessor(), Mock.Of(), + Mock.Of>(), publishedUrlProvider ?? Mock.Of(), + Mock.Of(), Mock.Of(), Mock.Of(), + Mock.Of(), umbracoContextAccessor, Mock.Of()); + + protected IUmbracoContextAccessor GetUmbracoContextAccessor(string urlAsString) + { + IPublishedSnapshot snapshot = GetPublishedSnapshot(); + + var uri = new Uri(urlAsString.Contains(Uri.SchemeDelimiter) + ? urlAsString + : $"http://example.com{urlAsString}"); + + IUmbracoContext umbracoContext = Mock.Of( + x => x.CleanedUmbracoUrl == uri + && x.Content == snapshot.Content + && x.PublishedSnapshot == snapshot); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + return umbracoContextAccessor; + } + + /// + /// Used as a property editor for any test property that has an editor alias called "Umbraco.Void.RTE" + /// + private class TestSimpleTinyMceValueConverter : SimpleTinyMceValueConverter + { + public override bool IsConverter(IPublishedPropertyType propertyType) + => propertyType.EditorAlias == "Umbraco.Void.RTE"; + } + + protected static DataType[] GetDefaultDataTypes() + { + var serializer = new ConfigurationEditorJsonSerializer(); + + // create data types, property types and content types + var dataType = + new DataType(new VoidEditor("Editor", Mock.Of()), serializer) { Id = 3 }; + + return new[] { dataType }; + } + + protected virtual ServiceContext CreateServiceContext(IContentType[] contentTypes, IMediaType[] mediaTypes, + IDataType[] dataTypes) + { + var contentTypeService = new Mock(); + contentTypeService.Setup(x => x.GetAll()).Returns(contentTypes); + contentTypeService.Setup(x => x.GetAll(It.IsAny())).Returns(contentTypes); + contentTypeService.Setup(x => x.Get(It.IsAny())) + .Returns((string alias) => contentTypes.FirstOrDefault(x => x.Alias.InvariantEquals(alias))); + + var mediaTypeService = new Mock(); + mediaTypeService.Setup(x => x.GetAll()).Returns(mediaTypes); + mediaTypeService.Setup(x => x.GetAll(It.IsAny())).Returns(mediaTypes); + mediaTypeService.Setup(x => x.Get(It.IsAny())) + .Returns((string alias) => mediaTypes.FirstOrDefault(x => x.Alias.InvariantEquals(alias))); + + var contentTypeServiceBaseFactory = new Mock(); + contentTypeServiceBaseFactory.Setup(x => x.For(It.IsAny())) + .Returns(contentTypeService.Object); + + var dataTypeServiceMock = new Mock(); + dataTypeServiceMock.Setup(x => x.GetAll()).Returns(dataTypes); + + return ServiceContext.CreatePartial( + dataTypeService: dataTypeServiceMock.Object, + memberTypeService: Mock.Of(), + memberService: Mock.Of(), + contentTypeService: contentTypeService.Object, + mediaTypeService: mediaTypeService.Object, + localizationService: Mock.Of(), + domainService: Mock.Of(), + fileService: Mock.Of() + ); + } + + /// + /// Creates a published snapshot and set the accessor to resolve the created one + /// + /// + protected IPublishedSnapshot GetPublishedSnapshot() + { + IPublishedSnapshot snapshot = SnapshotService.CreatePublishedSnapshot(null); + PublishedSnapshotAccessor.SetCurrent(snapshot); + return snapshot; + } + + /// + /// Initializes the with a source of data + /// + /// + /// + protected void InitializedCache( + IEnumerable contentNodeKits, + IContentType[] contentTypes, + IDataType[] dataTypes = null, + IEnumerable mediaNodeKits = null, + IMediaType[] mediaTypes = null) + { + // create a data source for NuCache + NuCacheContentService = new TestNuCacheContentService(contentNodeKits, mediaNodeKits); + + IRuntimeState runtime = Mock.Of(); + Mock.Get(runtime).Setup(x => x.Level).Returns(RuntimeLevel.Run); + + // create a service context + ServiceContext serviceContext = CreateServiceContext( + contentTypes ?? Array.Empty(), + mediaTypes ?? Array.Empty(), + dataTypes ?? GetDefaultDataTypes()); + + DataTypeService = serviceContext.DataTypeService; + ContentTypeService = serviceContext.ContentTypeService; + MediaTypeService = serviceContext.MediaTypeService; + DomainService = serviceContext.DomainService; + + // create a scope provider + IScopeProvider scopeProvider = Mock.Of(); + Mock.Get(scopeProvider) + .Setup(x => x.CreateScope( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Returns(Mock.Of); + + // create a published content type factory + PublishedContentTypeFactory = new PublishedContentTypeFactory( + PublishedModelFactory, + PropertyValueConverterCollection, + DataTypeService); + + ITypeFinder typeFinder = TestHelper.GetTypeFinder(); + + var nuCacheSettings = new NuCacheSettings(); + + // at last, create the complete NuCache snapshot service! + var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true }; + SnapshotService = new PublishedSnapshotService( + options, + Mock.Of(x => x.GetSyncBootState() == SyncBootState.WarmBoot), + new SimpleMainDom(), + serviceContext, + PublishedContentTypeFactory, + PublishedSnapshotAccessor, + VariationContextAccessor, + Mock.Of(), + NullLoggerFactory.Instance, + scopeProvider, + NuCacheContentService, + new TestDefaultCultureAccessor(), + Options.Create(GlobalSettings), + PublishedModelFactory, + TestHelper.GetHostingEnvironment(), + Options.Create(nuCacheSettings), + //ContentNestedDataSerializerFactory, + new ContentDataSerializer(new DictionaryOfPropertyDataSerializer())); + + // invariant is the current default + VariationContextAccessor.VariationContext = new VariationContext(); + + PublishedValueFallback = new PublishedValueFallback(serviceContext, VariationContextAccessor); + } + } +} diff --git a/tests/Umbraco.Tests.UnitTests/TestHelpers/TestNuCacheContentService.cs b/tests/Umbraco.Tests.UnitTests/TestHelpers/TestNuCacheContentService.cs new file mode 100644 index 000000000000..25542c5203dd --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/TestHelpers/TestNuCacheContentService.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache.Persistence; + +namespace Umbraco.Cms.Tests.UnitTests.TestHelpers +{ + public class TestNuCacheContentService : INuCacheContentService + { + private IPublishedModelFactory PublishedModelFactory { get; } = new NoopPublishedModelFactory(); + + public TestNuCacheContentService(params ContentNodeKit[] kits) + : this((IEnumerable)kits) + { } + + public TestNuCacheContentService(IEnumerable contentKits, IEnumerable mediaKits = null) + { + ContentKits = contentKits?.ToDictionary(x => x.Node.Id, x => x) ?? new Dictionary(); + MediaKits = mediaKits?.ToDictionary(x => x.Node.Id, x => x) ?? new Dictionary(); + } + + public Dictionary ContentKits { get; } + public Dictionary MediaKits { get; } + + // note: it is important to clone the returned kits, as the inner + // ContentNode is directly reused and modified by the snapshot service + public ContentNodeKit GetContentSource(int id) + => ContentKits.TryGetValue(id, out ContentNodeKit kit) ? kit.Clone(PublishedModelFactory) : default; + + public IEnumerable GetAllContentSources() + => ContentKits.Values + .OrderBy(x => x.Node.Level) + .ThenBy(x => x.Node.ParentContentId) + .ThenBy(x => x.Node.SortOrder) + .Select(x => x.Clone(PublishedModelFactory)); + + public IEnumerable GetBranchContentSources(int id) + => ContentKits.Values + .Where(x => x.Node.Path.EndsWith("," + id) || x.Node.Path.Contains("," + id + ",")) + .OrderBy(x => x.Node.Level) + .ThenBy(x => x.Node.ParentContentId) + .ThenBy(x => x.Node.SortOrder) + .Select(x => x.Clone(PublishedModelFactory)); + + public IEnumerable GetTypeContentSources(IEnumerable ids) + => ContentKits.Values + .Where(x => ids.Contains(x.ContentTypeId)) + .OrderBy(x => x.Node.Level) + .ThenBy(x => x.Node.ParentContentId) + .ThenBy(x => x.Node.SortOrder) + .Select(x => x.Clone(PublishedModelFactory)); + + public ContentNodeKit GetMediaSource(int id) + => MediaKits.TryGetValue(id, out ContentNodeKit kit) ? kit.Clone(PublishedModelFactory) : default; + + public IEnumerable GetAllMediaSources() + => MediaKits.Values + .OrderBy(x => x.Node.Level) + .ThenBy(x => x.Node.ParentContentId) + .ThenBy(x => x.Node.SortOrder) + .Select(x => x.Clone(PublishedModelFactory)); + + public IEnumerable GetBranchMediaSources(int id) + => MediaKits.Values + .Where(x => x.Node.Path.EndsWith("," + id) || x.Node.Path.Contains("," + id + ",")) + .OrderBy(x => x.Node.Level) + .ThenBy(x => x.Node.ParentContentId) + .ThenBy(x => x.Node.SortOrder) + .Select(x => x.Clone(PublishedModelFactory)); + + public IEnumerable GetTypeMediaSources(IEnumerable ids) + => MediaKits.Values + .Where(x => ids.Contains(x.ContentTypeId)) + .OrderBy(x => x.Node.Level) + .ThenBy(x => x.Node.ParentContentId) + .ThenBy(x => x.Node.SortOrder) + .Select(x => x.Clone(PublishedModelFactory)); + + public void DeleteContentItem(IContentBase item) => throw new NotImplementedException(); + public void DeleteContentItems(IEnumerable items) => throw new NotImplementedException(); + public void RefreshContent(IContent content) => throw new NotImplementedException(); + + public void RebuildDatabaseCacheIfSerializerChanged() => throw new NotImplementedException(); + public void RefreshMedia(IMedia media) => throw new NotImplementedException(); + public void RefreshMember(IMember member) => throw new NotImplementedException(); + public void Rebuild(IReadOnlyCollection contentTypeIds = null, IReadOnlyCollection mediaTypeIds = null, IReadOnlyCollection memberTypeIds = null) => throw new NotImplementedException(); + + public bool VerifyContentDbCache() => throw new NotImplementedException(); + public bool VerifyMediaDbCache() => throw new NotImplementedException(); + public bool VerifyMemberDbCache() => throw new NotImplementedException(); + } +} diff --git a/tests/Umbraco.Tests/Collections/StackQueueTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Collections/StackQueueTests.cs similarity index 93% rename from tests/Umbraco.Tests/Collections/StackQueueTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Collections/StackQueueTests.cs index 52d0370f0dfe..4caf7bd00536 100644 --- a/tests/Umbraco.Tests/Collections/StackQueueTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Collections/StackQueueTests.cs @@ -1,7 +1,7 @@ -using NUnit.Framework; +using NUnit.Framework; using Umbraco.Core.Collections; -namespace Umbraco.Tests.Collections +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Collections { [TestFixture] public class StackQueueTests @@ -16,7 +16,7 @@ public void Queue() } var expected = 0; - while(sq.Count > 0) + while (sq.Count > 0) { var next = sq.Dequeue(); Assert.AreEqual(expected, next); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByAliasTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByAliasTests.cs new file mode 100644 index 000000000000..0cdbce3da85b --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByAliasTests.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; +using Constants = Umbraco.Cms.Core.Constants; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing +{ + // TODO: We should be able to decouple this from the base db tests since we're just mocking the services now + + [TestFixture] + public class ContentFinderByAliasTests : UrlRoutingTestBase + { + + [TestCase("/this/is/my/alias", 1001)] + [TestCase("/anotheralias", 1001)] + [TestCase("/page2/alias", 10011)] + [TestCase("/2ndpagealias", 10011)] + [TestCase("/only/one/alias", 100111)] + [TestCase("/ONLY/one/Alias", 100111)] + [TestCase("/alias43", 100121)] + public async Task Lookup_By_Url_Alias(string urlAsString, int nodeMatch) + { + var umbracoContextAccessor = GetUmbracoContextAccessor(urlAsString); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + + var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + var lookup = + new ContentFinderByUrlAlias(Mock.Of>(), Mock.Of(), VariationContextAccessor, umbracoContextAccessor); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(frequest.PublishedContent.Id, nodeMatch); + } + } +} diff --git a/tests/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByAliasWithDomainsTests.cs similarity index 52% rename from tests/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByAliasWithDomainsTests.cs index 587000010737..8b77c4859930 100644 --- a/tests/Umbraco.Tests/Routing/ContentFinderByAliasWithDomainsTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByAliasWithDomainsTests.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -8,40 +9,18 @@ using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; using Constants = Umbraco.Cms.Core.Constants; -namespace Umbraco.Tests.Routing +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing { [TestFixture] - public class ContentFinderByAliasWithDomainsTests : ContentFinderByAliasTests + public class ContentFinderByAliasWithDomainsTests : UrlRoutingTestBase { - private PublishedContentType _publishedContentType; - protected override void Initialize() - { - base.Initialize(); - - var properties = new[] - { - new PublishedPropertyType( - propertyTypeAlias:"umbracoUrlAlias", - dataTypeId: Constants.DataTypes.Textbox, - isUserProperty:false, - variations: ContentVariation.Nothing, - propertyValueConverters:new PropertyValueConverterCollection(Enumerable.Empty()), - contentType:Mock.Of(), - publishedModelFactory:Mock.Of(), - factory:Mock.Of() - ) - }; - _publishedContentType = new PublishedContentType(Guid.NewGuid(), 0, "Doc", PublishedItemType.Content, Enumerable.Empty(), properties, ContentVariation.Nothing); - } - - protected override PublishedContentType GetPublishedContentTypeByAlias(string alias) - { - if (alias == "Doc") return _publishedContentType; - return null; - } [TestCase("http://domain1.com/this/is/my/alias", "de-DE", -1001)] // alias to domain's page fails - no alias on domain's home [TestCase("http://domain1.com/page2/alias", "de-DE", 10011)] // alias to sub-page works @@ -56,10 +35,11 @@ protected override PublishedContentType GetPublishedContentTypeByAlias(string al public async Task Lookup_By_Url_Alias_And_Domain(string inputUrl, string expectedCulture, int expectedNode) { //SetDomains1(); + var umbracoContextAccessor = GetUmbracoContextAccessor(inputUrl); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); - var umbracoContext = GetUmbracoContext(inputUrl); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var request = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + var request = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // must lookup domain publishedRouter.FindDomain(request); @@ -68,7 +48,7 @@ public async Task Lookup_By_Url_Alias_And_Domain(string inputUrl, string expecte Assert.AreEqual(expectedCulture, request.Culture); } - var finder = new ContentFinderByUrlAlias(LoggerFactory.CreateLogger(), Mock.Of(), VariationContextAccessor, GetUmbracoContextAccessor(umbracoContext)); + var finder = new ContentFinderByUrlAlias(Mock.Of>(), Mock.Of(), VariationContextAccessor, umbracoContextAccessor); var result = finder.TryFindContent(request); if (expectedNode > 0) diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByIdTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByIdTests.cs new file mode 100644 index 000000000000..1e5d95f42e01 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByIdTests.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing +{ + [TestFixture] + public class ContentFinderByIdTests : PublishedSnapshotServiceTestBase + { + [SetUp] + public override void Setup() + { + base.Setup(); + + string xml = PublishedContentXml.BaseWebTestXml(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + } + + [TestCase("/1046", 1046)] + public async Task Lookup_By_Id(string urlAsString, int nodeMatch) + { + var umbracoContextAccessor = GetUmbracoContextAccessor(urlAsString); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + var webRoutingSettings = new WebRoutingSettings(); + var lookup = new ContentFinderByIdPath(Options.Create(webRoutingSettings), Mock.Of>(), Mock.Of(), umbracoContextAccessor); + + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(frequest.PublishedContent.Id, nodeMatch); + } + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByPageIdQueryTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByPageIdQueryTests.cs new file mode 100644 index 000000000000..9aceaf766a63 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByPageIdQueryTests.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Web; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing +{ + [TestFixture] + public class ContentFinderByPageIdQueryTests : PublishedSnapshotServiceTestBase + { + [SetUp] + public override void Setup() + { + base.Setup(); + + string xml = PublishedContentXml.BaseWebTestXml(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + } + + [TestCase("/?umbPageId=1046", 1046)] + [TestCase("/?UMBPAGEID=1046", 1046)] + [TestCase("/default.aspx?umbPageId=1046", 1046)] // TODO: Should this match?? + [TestCase("/some/other/page?umbPageId=1046", 1046)] // TODO: Should this match?? + [TestCase("/some/other/page.aspx?umbPageId=1046", 1046)] // TODO: Should this match?? + public async Task Lookup_By_Page_Id(string urlAsString, int nodeMatch) + { + var umbracoContextAccessor = GetUmbracoContextAccessor(urlAsString); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + + var queryStrings = HttpUtility.ParseQueryString(umbracoContext.CleanedUmbracoUrl.Query); + + var mockRequestAccessor = new Mock(); + mockRequestAccessor.Setup(x => x.GetRequestValue("umbPageID")) + .Returns(queryStrings["umbPageID"]); + + var lookup = new ContentFinderByPageIdQuery(mockRequestAccessor.Object, umbracoContextAccessor); + + var result = lookup.TryFindContent(frequest); + + Assert.IsTrue(result); + Assert.AreEqual(frequest.PublishedContent.Id, nodeMatch); + } + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAndTemplateTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAndTemplateTests.cs new file mode 100644 index 000000000000..58bcbbefb4b8 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlAndTemplateTests.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.Common.Testing; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing +{ + [TestFixture] + public class ContentFinderByUrlAndTemplateTests : PublishedSnapshotServiceTestBase + { + private IFileService _fileService; + + [SetUp] + public override void Setup() + { + base.Setup(); + + string xml = PublishedContentXml.BaseWebTestXml(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + } + + protected override ServiceContext CreateServiceContext(IContentType[] contentTypes, IMediaType[] mediaTypes, IDataType[] dataTypes) + { + var serviceContext = base.CreateServiceContext(contentTypes, mediaTypes, dataTypes); + + var fileService = Mock.Get(serviceContext.FileService); + fileService.Setup(x => x.GetTemplate(It.IsAny())) + .Returns((string alias) => new Template(ShortStringHelper, alias, alias)); + + _fileService = fileService.Object; + + return serviceContext; + } + + [TestCase("/blah")] + [TestCase("/home/Sub1/blah")] + [TestCase("/Home/Sub1/Blah")] //different cases + public async Task Match_Document_By_Url_With_Template(string urlAsString) + { + GlobalSettings.HideTopLevelNodeFromPath = false; + + var umbracoContextAccessor = GetUmbracoContextAccessor(urlAsString); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + + var webRoutingSettings = new WebRoutingSettings(); + var lookup = new ContentFinderByUrlAndTemplate( + Mock.Of>(), + _fileService, + ContentTypeService, + umbracoContextAccessor, + Microsoft.Extensions.Options.Options.Create(webRoutingSettings)); + + var result = lookup.TryFindContent(frequest); + + IPublishedRequest request = frequest.Build(); + + Assert.IsTrue(result); + Assert.IsNotNull(frequest.PublishedContent); + var templateAlias = request.GetTemplateAlias(); + Assert.IsNotNull(templateAlias); + Assert.AreEqual("blah".ToUpperInvariant(), templateAlias.ToUpperInvariant()); + } + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlTests.cs new file mode 100644 index 000000000000..556eed962274 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlTests.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing +{ + [TestFixture] + public class ContentFinderByUrlTests : PublishedSnapshotServiceTestBase + { + private async Task<(ContentFinderByUrl finder, IPublishedRequestBuilder frequest)> GetContentFinder(string urlString) + { + string xml = PublishedContentXml.BaseWebTestXml(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + var umbracoContextAccessor = GetUmbracoContextAccessor(urlString); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + var lookup = new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor); + return (lookup, frequest); + } + + [TestCase("/", 1046)] + [TestCase("/Sub1", 1173)] + [TestCase("/sub1", 1173)] + [TestCase("/home/sub1", -1)] // should fail + + // these two are special. getNiceUrl(1046) returns "/" but getNiceUrl(1172) cannot also return "/" so + // we've made it return "/test-page" => we have to support that URL back in the lookup... + [TestCase("/home", 1046)] + [TestCase("/test-page", 1172)] + public async Task Match_Document_By_Url_Hide_Top_Level(string urlString, int expectedId) + { + GlobalSettings.HideTopLevelNodeFromPath = true; + + var lookup = await GetContentFinder(urlString); + + Assert.IsTrue(GlobalSettings.HideTopLevelNodeFromPath); + + // FIXME: debugging - going further down, the routes cache is NOT empty?! + if (urlString == "/home/sub1") + System.Diagnostics.Debugger.Break(); + + var result = lookup.finder.TryFindContent(lookup.frequest); + + if (expectedId > 0) + { + Assert.IsTrue(result); + Assert.AreEqual(expectedId, lookup.frequest.PublishedContent.Id); + } + else + { + Assert.IsFalse(result); + } + } + + [TestCase("/", 1046)] + [TestCase("/home", 1046)] + [TestCase("/home/Sub1", 1173)] + [TestCase("/Home/Sub1", 1173)] //different cases + public async Task Match_Document_By_Url(string urlString, int expectedId) + { + GlobalSettings.HideTopLevelNodeFromPath = false; + + var lookup = await GetContentFinder(urlString); + + Assert.IsFalse(GlobalSettings.HideTopLevelNodeFromPath); + + var result = lookup.finder.TryFindContent(lookup.frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, lookup.frequest.PublishedContent.Id); + } + /// + /// This test handles requests with special characters in the URL. + /// + /// + /// + [TestCase("/", 1046)] + [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] + [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] + public async Task Match_Document_By_Url_With_Special_Characters(string urlString, int expectedId) + { + GlobalSettings.HideTopLevelNodeFromPath = false; + + var lookup = await GetContentFinder(urlString); + + var result = lookup.finder.TryFindContent(lookup.frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, lookup.frequest.PublishedContent.Id); + } + + /// + /// This test handles requests with a hostname associated. + /// The logic for handling this goes through the DomainHelper and is a bit different + /// from what happens in a normal request - so it has a separate test with a mocked + /// hostname added. + /// + /// + /// + [TestCase("/", 1046)] + [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] + [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] + public async Task Match_Document_By_Url_With_Special_Characters_Using_Hostname(string urlString, int expectedId) + { + GlobalSettings.HideTopLevelNodeFromPath = false; + + var lookup = await GetContentFinder(urlString); + + lookup.frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite", -1, "en-US", false), new Uri("http://mysite/"))); + + var result = lookup.finder.TryFindContent(lookup.frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, lookup.frequest.PublishedContent.Id); + } + + /// + /// This test handles requests with a hostname with special characters associated. + /// The logic for handling this goes through the DomainHelper and is a bit different + /// from what happens in a normal request - so it has a separate test with a mocked + /// hostname added. + /// + /// + /// + [TestCase("/æøå/", 1046)] + [TestCase("/æøå/home/sub1", 1173)] + [TestCase("/æøå/home/sub1/custom-sub-3-with-accént-character", 1179)] + [TestCase("/æøå/home/sub1/custom-sub-4-with-æøå", 1180)] + public async Task Match_Document_By_Url_With_Special_Characters_In_Hostname(string urlString, int expectedId) + { + GlobalSettings.HideTopLevelNodeFromPath = false; + + var lookup = await GetContentFinder(urlString); + + lookup.frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite/æøå", -1, "en-US", false), new Uri("http://mysite/æøå"))); + + var result = lookup.finder.TryFindContent(lookup.frequest); + + Assert.IsTrue(result); + Assert.AreEqual(expectedId, lookup.frequest.PublishedContent.Id); + } + } +} diff --git a/tests/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlWithDomainsTests.cs similarity index 83% rename from tests/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlWithDomainsTests.cs index f9ea04a2886b..d99214038649 100644 --- a/tests/Umbraco.Tests/Routing/ContentFinderByUrlWithDomainsTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/ContentFinderByUrlWithDomainsTests.cs @@ -1,42 +1,47 @@ using System.Threading.Tasks; using Microsoft.Extensions.Logging; +using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Routing; +using Umbraco.Extensions; -namespace Umbraco.Tests.Routing +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing { [TestFixture] public class ContentFinderByUrlWithDomainsTests : UrlRoutingTestBase { - void SetDomains3() + private void SetDomains3() { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangDeId, RootContentId = 1001, LanguageIsoCode = "de-DE"} - }); + var domainService = Mock.Get(DomainService); + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] + { + new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangDeId, RootContentId = 1001, LanguageIsoCode = "de-DE"} + }); } - void SetDomains4() + private void SetDomains4() { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, - new UmbracoDomain("domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} - }); - + var domainService = Mock.Get(DomainService); + + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] + { + new UmbracoDomain("domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, + new UmbracoDomain("domain1.com/en") {Id = 2, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("domain1.com/fr") {Id = 3, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain3.com/") {Id = 4, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/en") {Id = 5, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/fr") {Id = 6, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} + }); } protected override string GetXmlContent(int templateId) - { - return @" + => @" @@ -115,7 +120,6 @@ protected override string GetXmlContent(int templateId) "; - } [TestCase("http://domain1.com/", 1001)] [TestCase("http://domain1.com/1001-1", 10011)] @@ -125,16 +129,17 @@ public async Task Lookup_SingleDomain(string url, int expectedId) { SetDomains3(); - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = true }; + GlobalSettings.HideTopLevelNodeFromPath = true; - var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettings); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext), Factory); + var umbracoContextAccessor = GetUmbracoContextAccessor(url); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // must lookup domain else lookup by URL fails publishedRouter.FindDomain(frequest); - var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); + var lookup = new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor); var result = lookup.TryFindContent(frequest); Assert.IsTrue(result); Assert.AreEqual(expectedId, frequest.PublishedContent.Id); @@ -164,19 +169,20 @@ public async Task Lookup_NestedDomains(string url, int expectedId, string expect SetDomains4(); // defaults depend on test environment - expectedCulture = expectedCulture ?? System.Threading.Thread.CurrentThread.CurrentUICulture.Name; + expectedCulture ??= System.Threading.Thread.CurrentThread.CurrentUICulture.Name; - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = true }; + GlobalSettings.HideTopLevelNodeFromPath = true; - var umbracoContext = GetUmbracoContext(url, globalSettings:globalSettings); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext), Factory); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + var umbracoContextAccessor = GetUmbracoContextAccessor(url); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // must lookup domain else lookup by URL fails publishedRouter.FindDomain(frequest); Assert.AreEqual(expectedCulture, frequest.Culture); - var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); + var lookup = new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor); var result = lookup.TryFindContent(frequest); Assert.IsTrue(result); Assert.AreEqual(expectedId, frequest.PublishedContent.Id); diff --git a/tests/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/DomainsAndCulturesTests.cs similarity index 72% rename from tests/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/DomainsAndCulturesTests.cs index 73a88abfabb9..ccaeee9322bd 100644 --- a/tests/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/DomainsAndCulturesTests.cs @@ -1,124 +1,151 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Routing; +using Umbraco.Extensions; -namespace Umbraco.Tests.Routing +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing { [TestFixture] - internal class DomainsAndCulturesTests : UrlRoutingTestBase + public class DomainsAndCulturesTests : UrlRoutingTestBase { - protected override void Compose() + private void SetDomains1() { - base.Compose(); + var domainService = Mock.Get(DomainService); - Builder.Services.AddTransient(); + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] + { + new UmbracoDomain("domain1.com/") + { + Id = 1, + LanguageId = LangDeId, + RootContentId = 1001, + LanguageIsoCode = "de-DE" + }, + new UmbracoDomain("domain1.com/en") + { + Id = 2, + LanguageId = LangEngId, + RootContentId = 10011, + LanguageIsoCode = "en-US" + }, + new UmbracoDomain("domain1.com/fr") + { + Id = 3, + LanguageId = LangFrId, + RootContentId = 10012, + LanguageIsoCode = "fr-FR" + } + }); } - private void SetDomains1() + private void SetDomains2() { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com/") - { - Id = 1, - LanguageId = LangDeId, - RootContentId = 1001, - LanguageIsoCode = "de-DE" - }, - new UmbracoDomain("domain1.com/en") - { - Id = 1, - LanguageId = LangEngId, - RootContentId = 10011, - LanguageIsoCode = "en-US" - }, - new UmbracoDomain("domain1.com/fr") + var domainService = Mock.Get(DomainService); + + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] { - Id = 1, - LanguageId = LangFrId, - RootContentId = 10012, - LanguageIsoCode = "fr-FR" - } - }); + new UmbracoDomain("domain1.com/") + { + Id = 1, + LanguageId = LangDeId, + RootContentId = 1001, + LanguageIsoCode = "de-DE" + }, + new UmbracoDomain("domain1.com/en") + { + Id = 2, + LanguageId = LangEngId, + RootContentId = 10011, + LanguageIsoCode = "en-US" + }, + new UmbracoDomain("domain1.com/fr") + { + Id = 3, + LanguageId = LangFrId, + RootContentId = 10012, + LanguageIsoCode = "fr-FR" + }, + new UmbracoDomain("*1001") + { + Id = 4, + LanguageId = LangDeId, + RootContentId = 1001, + LanguageIsoCode = "de-DE" + }, + new UmbracoDomain("*10011") + { + Id = 5, + LanguageId = LangCzId, + RootContentId = 10011, + LanguageIsoCode = "cs-CZ" + }, + new UmbracoDomain("*100112") + { + Id = 6, + LanguageId = LangNlId, + RootContentId = 100112, + LanguageIsoCode = "nl-NL" + }, + new UmbracoDomain("*1001122") + { + Id = 7, + LanguageId = LangDkId, + RootContentId = 1001122, + LanguageIsoCode = "da-DK" + }, + new UmbracoDomain("*10012") + { + Id = 8, + LanguageId = LangNlId, + RootContentId = 10012, + LanguageIsoCode = "nl-NL" + }, + new UmbracoDomain("*10031") + { + Id = 9, + LanguageId = LangNlId, + RootContentId =10031, + LanguageIsoCode = "nl-NL" + } + }); } - private void SetDomains2() + // domains such as "/en" are natively supported, and when instanciating + // DomainAndUri for them, the host will come from the current request + // + private void SetDomains3() { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com/") - { - Id = 1, - LanguageId = LangDeId, - RootContentId = 1001, - LanguageIsoCode = "de-DE" - }, - new UmbracoDomain("domain1.com/en") - { - Id = 1, - LanguageId = LangEngId, - RootContentId = 10011, - LanguageIsoCode = "en-US" - }, - new UmbracoDomain("domain1.com/fr") - { - Id = 1, - LanguageId = LangFrId, - RootContentId = 10012, - LanguageIsoCode = "fr-FR" - }, - new UmbracoDomain("*1001") - { - Id = 1, - LanguageId = LangDeId, - RootContentId = 1001, - LanguageIsoCode = "de-DE" - }, - new UmbracoDomain("*10011") - { - Id = 1, - LanguageId = LangCzId, - RootContentId = 10011, - LanguageIsoCode = "cs-CZ" - }, - new UmbracoDomain("*100112") - { - Id = 1, - LanguageId = LangNlId, - RootContentId = 100112, - LanguageIsoCode = "nl-NL" - }, - new UmbracoDomain("*1001122") - { - Id = 1, - LanguageId = LangDkId, - RootContentId = 1001122, - LanguageIsoCode = "da-DK" - }, - new UmbracoDomain("*10012") - { - Id = 1, - LanguageId = LangNlId, - RootContentId = 10012, - LanguageIsoCode = "nl-NL" - }, - new UmbracoDomain("*10031") + var domainService = Mock.Get(DomainService); + + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] { - Id = 1, - LanguageId = LangNlId, - RootContentId =10031, - LanguageIsoCode = "nl-NL" - } - }); + new UmbracoDomain("/en") + { + Id = 1, + LanguageId = LangEngId, + RootContentId = 10011, + LanguageIsoCode = "en-US" + }, + new UmbracoDomain("/fr") + { + Id = 2, + LanguageId = LangFrId, + RootContentId = 10012, + LanguageIsoCode = "fr-FR" + } + }); } protected override string GetXmlContent(int templateId) - { - return @" + => @" @@ -250,7 +277,6 @@ protected override string GetXmlContent(int templateId) "; - } #region Cases [TestCase("http://domain1.com/", "de-DE", 1001)] @@ -265,18 +291,19 @@ public async Task DomainAndCulture(string inputUrl, string expectedCulture, int { SetDomains1(); - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; - var umbracoContext = GetUmbracoContext(inputUrl, globalSettings:globalSettings); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext), Factory); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + var umbracoContextAccessor = GetUmbracoContextAccessor(inputUrl); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // lookup domain publishedRouter.FindDomain(frequest); Assert.AreEqual(expectedCulture, frequest.Culture); - var finder = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); + var finder = new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor); var result = finder.TryFindContent(frequest); Assert.IsTrue(result); @@ -311,19 +338,20 @@ public async Task DomainAndCultureWithWildcards(string inputUrl, string expected SetDomains2(); // defaults depend on test environment - expectedCulture = expectedCulture ?? System.Threading.Thread.CurrentThread.CurrentUICulture.Name; + expectedCulture ??= System.Threading.Thread.CurrentThread.CurrentUICulture.Name; - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; - var umbracoContext = GetUmbracoContext(inputUrl, globalSettings:globalSettings); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext), Factory); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + var umbracoContextAccessor = GetUmbracoContextAccessor(inputUrl); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // lookup domain publishedRouter.FindDomain(frequest); // find document - var finder = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); + var finder = new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor); var result = finder.TryFindContent(frequest); // apply wildcard domain @@ -333,29 +361,7 @@ public async Task DomainAndCultureWithWildcards(string inputUrl, string expected Assert.AreEqual(expectedCulture, frequest.Culture); Assert.AreEqual(frequest.PublishedContent.Id, expectedNode); } - // domains such as "/en" are natively supported, and when instanciating - // DomainAndUri for them, the host will come from the current request - // - private void SetDomains3() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("/en") - { - Id = 1, - LanguageId = LangEngId, - RootContentId = 10011, - LanguageIsoCode = "en-US" - }, - new UmbracoDomain("/fr") - { - Id = 1, - LanguageId = LangFrId, - RootContentId = 10012, - LanguageIsoCode = "fr-FR" - } - }); - } + #region Cases [TestCase("http://domain1.com/en", "en-US", 10011)] @@ -367,10 +373,13 @@ public async Task DomainGeneric(string inputUrl, string expectedCulture, int exp { SetDomains3(); - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; - var umbracoContext = GetUmbracoContext(inputUrl, globalSettings:globalSettings); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext), Factory); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + GlobalSettings.HideTopLevelNodeFromPath = false; + + + var umbracoContextAccessor = GetUmbracoContextAccessor(inputUrl); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); // lookup domain publishedRouter.FindDomain(frequest); @@ -378,7 +387,7 @@ public async Task DomainGeneric(string inputUrl, string expectedCulture, int exp Assert.AreEqual(expectedCulture, frequest.Culture); - var finder = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); + var finder = new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor); var result = finder.TryFindContent(frequest); Assert.IsTrue(result); diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/GetContentUrlsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/GetContentUrlsTests.cs new file mode 100644 index 000000000000..f54442e4675d --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/GetContentUrlsTests.cs @@ -0,0 +1,216 @@ +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Builders; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing +{ + [TestFixture] + public class GetContentUrlsTests : PublishedSnapshotServiceTestBase + { + private WebRoutingSettings _webRoutingSettings; + private RequestHandlerSettings _requestHandlerSettings; + + [SetUp] + public override void Setup() + { + base.Setup(); + + _webRoutingSettings = new WebRoutingSettings(); + _requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; + + GlobalSettings.HideTopLevelNodeFromPath = false; + + string xml = PublishedContentXml.BaseWebTestXml(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + } + + private ILocalizedTextService GetTextService() + { + var textService = new Mock(); + textService.Setup(x => x.Localize( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny>() + )) + .Returns((string key, string alias, CultureInfo culture, IDictionary args) + => $"{key}/{alias}"); + + return textService.Object; + } + + private ILocalizationService GetLangService(params string[] isoCodes) + { + var allLangs = isoCodes + .Select(CultureInfo.GetCultureInfo) + .Select(culture => new Language(GlobalSettings, culture.Name) + { + CultureName = culture.DisplayName, + IsDefault = true, + IsMandatory = true + }).ToArray(); + + + var langServiceMock = new Mock(); + langServiceMock.Setup(x => x.GetAllLanguages()).Returns(allLangs); + langServiceMock.Setup(x => x.GetDefaultLanguageIsoCode()).Returns(allLangs.First(x=>x.IsDefault).IsoCode); + + return langServiceMock.Object; + } + + [Test] + public async Task Content_Not_Published() + { + var contentType = ContentTypeBuilder.CreateBasicContentType(); + var content = ContentBuilder.CreateBasicContent(contentType); + content.Id = 1046; // FIXME: we are using this ID only because it's built into the test XML published cache + content.Path = "-1,1046"; + + var umbracoContextAccessor = GetUmbracoContextAccessor("http://localhost:8000"); + var publishedRouter = CreatePublishedRouter( + umbracoContextAccessor, + new[] { new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor) }); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, _requestHandlerSettings, _webRoutingSettings, out UriUtility uriUtility); + + var urls = (await content.GetContentUrlsAsync( + publishedRouter, + umbracoContext, + GetLangService("en-US", "fr-FR"), + GetTextService(), + Mock.Of(), + VariationContextAccessor, + Mock.Of>(), + uriUtility, + urlProvider)).ToList(); + + Assert.AreEqual(1, urls.Count); + Assert.AreEqual("content/itemNotPublished", urls[0].Text); + Assert.IsFalse(urls[0].IsUrl); + } + + [Test] + public async Task Invariant_Root_Content_Published_No_Domains() + { + var contentType = ContentTypeBuilder.CreateBasicContentType(); + var content = ContentBuilder.CreateBasicContent(contentType); + content.Id = 1046; // FIXME: we are using this ID only because it's built into the test XML published cache + content.Path = "-1,1046"; + content.Published = true; + + var umbracoContextAccessor = GetUmbracoContextAccessor("http://localhost:8000"); + var publishedRouter = CreatePublishedRouter( + umbracoContextAccessor, + new[] { new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor) }); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, _requestHandlerSettings, _webRoutingSettings, out UriUtility uriUtility); + + var urls = (await content.GetContentUrlsAsync( + publishedRouter, + umbracoContext, + GetLangService("en-US", "fr-FR"), + GetTextService(), + Mock.Of(), + VariationContextAccessor, + Mock.Of>(), + uriUtility, + urlProvider)).ToList(); + + + Assert.AreEqual(2, urls.Count); + + var enUrl = urls.First(x => x.Culture == "en-US"); + + Assert.AreEqual("/home/", enUrl.Text); + Assert.AreEqual("en-US", enUrl.Culture); + Assert.IsTrue(enUrl.IsUrl); + + var frUrl = urls.First(x => x.Culture == "fr-FR"); + + Assert.IsFalse(frUrl.IsUrl); + } + + [Test] + public async Task Invariant_Child_Content_Published_No_Domains() + { + var contentType = ContentTypeBuilder.CreateBasicContentType(); + var parent = ContentBuilder.CreateBasicContent(contentType); + parent.Id = 1046; // FIXME: we are using this ID only because it's built into the test XML published cache + parent.Name = "home"; + parent.Path = "-1,1046"; + parent.Published = true; + var child = ContentBuilder.CreateBasicContent(contentType); + child.Name = "sub1"; + child.Id = 1173; // FIXME: we are using this ID only because it's built into the test XML published cache + child.Path = "-1,1046,1173"; + child.Published = true; + + + var umbracoContextAccessor = GetUmbracoContextAccessor("http://localhost:8000"); + var publishedRouter = CreatePublishedRouter( + umbracoContextAccessor, + new[] { new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor) }); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + + + var localizationService = GetLangService("en-US", "fr-FR"); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, _requestHandlerSettings, _webRoutingSettings, out UriUtility uriUtility); + + var urls = (await child.GetContentUrlsAsync( + publishedRouter, + umbracoContext, + localizationService, + GetTextService(), + Mock.Of(), + VariationContextAccessor, + Mock.Of>(), + uriUtility, + urlProvider)).ToList(); + + Assert.AreEqual(2, urls.Count); + + var enUrl = urls.First(x => x.Culture == "en-US"); + + Assert.AreEqual("/home/sub1/", enUrl.Text); + Assert.AreEqual("en-US", enUrl.Culture); + Assert.IsTrue(enUrl.IsUrl); + + var frUrl = urls.First(x => x.Culture == "fr-FR"); + + Assert.IsFalse(frUrl.IsUrl); + } + + // TODO: We need a lot of tests here, the above was just to get started with being able to unit test this method + // * variant URLs without domains assigned, what happens? + // * variant URLs with domains assigned, but also having more languages installed than there are domains/cultures assigned + // * variant URLs with an ancestor culture unpublished + // * invariant URLs with ancestors as variants + // * ... probably a lot more + + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/PublishedRouterTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/PublishedRouterTests.cs new file mode 100644 index 000000000000..b1db80232a2b --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/PublishedRouterTests.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Web; +using Umbraco.Cms.Tests.Common; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing +{ + [TestFixture] + public class PublishedRouterTests + { + private PublishedRouter CreatePublishedRouter(IUmbracoContextAccessor umbracoContextAccessor) + => new PublishedRouter( + Microsoft.Extensions.Options.Options.Create(new WebRoutingSettings()), + new ContentFinderCollection(() => Enumerable.Empty()), + new TestLastChanceFinder(), + new TestVariationContextAccessor(), + Mock.Of(), + Mock.Of>(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + Mock.Of(), + umbracoContextAccessor, + Mock.Of()); + + private IUmbracoContextAccessor GetUmbracoContextAccessor() + { + var uri = new Uri("http://example.com"); + var umbracoContext = Mock.Of(x => x.CleanedUmbracoUrl == uri); + var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); + return umbracoContextAccessor; + } + + [Test] + public async Task ConfigureRequest_Returns_False_Without_HasPublishedContent() + { + var umbracoContextAccessor = GetUmbracoContextAccessor(); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var request = await publishedRouter.CreateRequestAsync(umbracoContextAccessor.GetRequiredUmbracoContext().CleanedUmbracoUrl); + var result = publishedRouter.BuildRequest(request); + + Assert.IsFalse(result.Success()); + } + + [Test] + public async Task ConfigureRequest_Returns_False_When_IsRedirect() + { + var umbracoContextAccessor = GetUmbracoContextAccessor(); + var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); + var request = await publishedRouter.CreateRequestAsync(umbracoContextAccessor.GetRequiredUmbracoContext().CleanedUmbracoUrl); + var content = GetPublishedContentMock(); + request.SetPublishedContent(content.Object); + request.SetCulture("en-AU"); + request.SetRedirect("/hello"); + var result = publishedRouter.BuildRequest(request); + + Assert.IsFalse(result.Success()); + } + + private Mock GetPublishedContentMock() + { + var pc = new Mock(); + pc.Setup(content => content.Id).Returns(1); + pc.Setup(content => content.Name).Returns("test"); + pc.Setup(content => content.CreateDate).Returns(DateTime.Now); + pc.Setup(content => content.UpdateDate).Returns(DateTime.Now); + pc.Setup(content => content.Path).Returns("-1,1"); + pc.Setup(content => content.Parent).Returns(() => null); + pc.Setup(content => content.Properties).Returns(new Collection()); + pc.Setup(content => content.ContentType).Returns(new PublishedContentType(Guid.NewGuid(), 22, "anything", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing)); + return pc; + } + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs new file mode 100644 index 000000000000..59884882bfa1 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.Extensions.Logging; +using NUnit.Framework; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing +{ + [TestFixture] + public class UrlProviderWithHideTopLevelNodeFromPathTests : PublishedSnapshotServiceTestBase + { + [SetUp] + public override void Setup() + { + base.Setup(); + + string xml = PublishedContentXml.BaseWebTestXml(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + GlobalSettings.HideTopLevelNodeFromPath = true; + } + + [TestCase(1046, "/")] + [TestCase(1173, "/sub1/")] + [TestCase(1174, "/sub1/sub2/")] + [TestCase(1176, "/sub1/sub-3/")] + [TestCase(1177, "/sub1/custom-sub-1/")] + [TestCase(1178, "/sub1/custom-sub-2/")] + [TestCase(1175, "/sub-2/")] + [TestCase(1172, "/test-page/")] // not hidden because not first root + public void Get_Url_Hiding_Top_Level(int nodeId, string niceUrlMatch) + { + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; + + var umbracoContextAccessor = GetUmbracoContextAccessor("/test"); + + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); + + var result = urlProvider.GetUrl(nodeId); + Assert.AreEqual(niceUrlMatch, result); + } + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs new file mode 100644 index 000000000000..8c68aa5c36a7 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs @@ -0,0 +1,336 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; +using Umbraco.Cms.Tests.Common; +using Umbraco.Cms.Tests.Common.Builders; +using Umbraco.Cms.Tests.Common.Builders.Extensions; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing +{ + [TestFixture] + public class UrlProviderWithoutHideTopLevelNodeFromPathTests : PublishedSnapshotServiceTestBase + { + private const string CacheKeyPrefix = "NuCache.ContentCache.RouteByContent"; + + [SetUp] + public override void Setup() + { + base.Setup(); + + + + GlobalSettings.HideTopLevelNodeFromPath = false; + } + + private void PopulateCache(string culture = "fr-FR") + { + var dataTypes = GetDefaultDataTypes(); + var propertyDataTypes = new Dictionary + { + // we only have one data type for this test which will be resolved with string empty. + [string.Empty] = dataTypes[0] + }; + var contentType1 = new ContentType(ShortStringHelper, -1); + + ContentData rootData = new ContentDataBuilder() + .WithName("Page" + Guid.NewGuid()) + .WithCultureInfos(new Dictionary + { + [culture] = new CultureVariation + { + Name = "root", + IsDraft = true, + Date = DateTime.Now, + UrlSegment = "root" + }, + }) + .Build(ShortStringHelper, propertyDataTypes, contentType1, "alias"); + + ContentNodeKit root = ContentNodeKitBuilder.CreateWithContent( + contentType1.Id, + 9876, $"-1,9876", + draftData: rootData, + publishedData: rootData); + + ContentData parentData = new ContentDataBuilder() + .WithName("Page" + Guid.NewGuid()) + .WithCultureInfos(new Dictionary + { + [culture] = new CultureVariation + { + Name = "home", + IsDraft = true, + Date = DateTime.Now, + UrlSegment = "home" + }, + }) + .Build(); + + ContentNodeKit parent = ContentNodeKitBuilder.CreateWithContent( + contentType1.Id, + 5432, $"-1,9876,5432", + parentContentId: 9876, + draftData: parentData, + publishedData: parentData); + + ContentData contentData = new ContentDataBuilder() + .WithName("Page" + Guid.NewGuid()) + .WithCultureInfos(new Dictionary + { + [culture] = new CultureVariation + { + Name = "name-fr2", + IsDraft = true, + Date = DateTime.Now, + UrlSegment = "test-fr" + }, + }) + .Build(); + + ContentNodeKit content = ContentNodeKitBuilder.CreateWithContent( + contentType1.Id, + 1234, $"-1,9876,5432,1234", + parentContentId: 5432, + draftData: contentData, + publishedData: contentData); + + InitializedCache(new[] { root, parent, content }, new[] { contentType1 }, dataTypes: dataTypes); + } + + private void SetDomains1() + { + var domainService = Mock.Get(DomainService); + + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] + { + new UmbracoDomain("http://example.us/") {Id = 1, RootContentId = 9876, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://example.fr/") {Id = 2, RootContentId = 9876, LanguageIsoCode = "fr-FR"} + }); + } + + /// + /// This checks that when we retrieve a NiceUrl for multiple items that there are no issues with cache overlap + /// and that they are all cached correctly. + /// + [Test] + public void Ensure_Cache_Is_Correct() + { + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = false }; + + string xml = PublishedContentXml.BaseWebTestXml(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + var umbracoContextAccessor = GetUmbracoContextAccessor("/test"); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); + + var samples = new Dictionary { + { 1046, "/home" }, + { 1173, "/home/sub1" }, + { 1174, "/home/sub1/sub2" }, + { 1176, "/home/sub1/sub-3" }, + { 1177, "/home/sub1/custom-sub-1" }, + { 1178, "/home/sub1/custom-sub-2" }, + { 1175, "/home/sub-2" }, + { 1172, "/test-page" } + }; + + foreach (var sample in samples) + { + var result = urlProvider.GetUrl(sample.Key); + Assert.AreEqual(sample.Value, result); + } + + var randomSample = new KeyValuePair(1177, "/home/sub1/custom-sub-1"); + for (int i = 0; i < 5; i++) + { + var result = urlProvider.GetUrl(randomSample.Key); + Assert.AreEqual(randomSample.Value, result); + } + + + var cache = (FastDictionaryAppCache)umbracoContext.PublishedSnapshot.ElementsCache; + var cachedRoutes = cache.Keys.Where(x => x.StartsWith(CacheKeyPrefix)).ToList(); + Assert.AreEqual(8, cachedRoutes.Count); + + foreach (var sample in samples) + { + var cacheKey = $"{CacheKeyPrefix}[P:{sample.Key}]"; + var found = (string)cache.Get(cacheKey); + Assert.IsNotNull(found); + Assert.AreEqual(sample.Value, found); + } + } + + [TestCase(1046, "/home/")] + [TestCase(1173, "/home/sub1/")] + [TestCase(1174, "/home/sub1/sub2/")] + [TestCase(1176, "/home/sub1/sub-3/")] + [TestCase(1177, "/home/sub1/custom-sub-1/")] + [TestCase(1178, "/home/sub1/custom-sub-2/")] + [TestCase(1175, "/home/sub-2/")] + [TestCase(1172, "/test-page/")] + public void Get_Url_Not_Hiding_Top_Level(int nodeId, string niceUrlMatch) + { + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; + + string xml = PublishedContentXml.BaseWebTestXml(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + var umbracoContextAccessor = GetUmbracoContextAccessor("/test"); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); + + var result = urlProvider.GetUrl(nodeId); + Assert.AreEqual(niceUrlMatch, result); + } + + [Test] + [TestCase("fr-FR", ExpectedResult = "#")] // Non default cultures cannot return urls + [TestCase("en-US", ExpectedResult = "/root/home/test-fr/")] // Default culture can return urls + public string Get_Url_For_Culture_Variant_Without_Domains_Non_Current_Url(string culture) + { + const string currentUri = "http://example.us/test"; + + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; + + PopulateCache(culture); + + var umbracoContextAccessor = GetUmbracoContextAccessor(currentUri); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); + + + //even though we are asking for a specific culture URL, there are no domains assigned so all that can be returned is a normal relative URL. + var url = urlProvider.GetUrl(1234, culture: culture); + + return url; + } + + /// + /// This tests DefaultUrlProvider.GetUrl with a specific culture when the current URL is the culture specific domain + /// + [Test] + public void Get_Url_For_Culture_Variant_With_Current_Url() + { + const string currentUri = "http://example.fr/test"; + + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; + + PopulateCache(); + + SetDomains1(); + + var umbracoContextAccessor = GetUmbracoContextAccessor(currentUri); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); + + var url = urlProvider.GetUrl(1234, culture: "fr-FR"); + + Assert.AreEqual("/home/test-fr/", url); + } + + /// + /// This tests DefaultUrlProvider.GetUrl with a specific culture when the current URL is not the culture specific domain + /// + [Test] + public void Get_Url_For_Culture_Variant_Non_Current_Url() + { + const string currentUri = "http://example.us/test"; + + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; + + PopulateCache(); + + SetDomains1(); + + var umbracoContextAccessor = GetUmbracoContextAccessor(currentUri); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); + var url = urlProvider.GetUrl(1234, culture: "fr-FR"); + + //the current uri is not the culture specific domain we want, so the result is an absolute path to the culture specific domain + Assert.AreEqual("http://example.fr/home/test-fr/", url); + } + + [Test] + public void Get_Url_Relative_Or_Absolute() + { + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; + + string xml = PublishedContentXml.BaseWebTestXml(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + var umbracoContextAccessor = GetUmbracoContextAccessor("http://example.com/test"); + + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); + + Assert.AreEqual("/home/sub1/custom-sub-1/", urlProvider.GetUrl(1177)); + + urlProvider.Mode = UrlMode.Absolute; + Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", urlProvider.GetUrl(1177)); + } + + [Test] + public void Get_Url_Unpublished() + { + var requestHandlerSettings = new RequestHandlerSettings(); + + string xml = PublishedContentXml.BaseWebTestXml(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + var umbracoContextAccessor = GetUmbracoContextAccessor("http://example.com/test"); + + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); + + //mock the Umbraco settings that we need + + Assert.AreEqual("#", urlProvider.GetUrl(999999)); + + urlProvider.Mode = UrlMode.Absolute; + + Assert.AreEqual("#", urlProvider.GetUrl(999999)); + } + } +} diff --git a/tests/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlRoutingTestBase.cs similarity index 81% rename from tests/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlRoutingTestBase.cs index 82dea51d1c1c..65eea64f75c4 100644 --- a/tests/Umbraco.Tests/Routing/ContentFinderByAliasTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlRoutingTestBase.cs @@ -1,68 +1,55 @@ -using System; +using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Routing; -using Constants = Umbraco.Cms.Core.Constants; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; -namespace Umbraco.Tests.Routing +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing { - // TODO: We should be able to decouple this from the base db tests since we're just mocking the services now - [TestFixture] - public class ContentFinderByAliasTests : UrlRoutingTestBase + public abstract class UrlRoutingTestBase : PublishedSnapshotServiceTestBase { - private PublishedContentType _publishedContentType; - - protected override void Initialize() + [SetUp] + public override void Setup() { - base.Initialize(); + base.Setup(); - var properties = new[] - { - new PublishedPropertyType("umbracoUrlAlias", Constants.DataTypes.Textbox, false, ContentVariation.Nothing, - new PropertyValueConverterCollection(Enumerable.Empty()), - Mock.Of(), - Mock.Of()), - }; - _publishedContentType = new PublishedContentType(Guid.NewGuid(), 0, "Doc", PublishedItemType.Content, Enumerable.Empty(), properties, ContentVariation.Nothing); - } + string xml = GetXmlContent(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); - protected override PublishedContentType GetPublishedContentTypeByAlias(string alias) - { - if (alias == "Doc") return _publishedContentType; - return null; } - [TestCase("/this/is/my/alias", 1001)] - [TestCase("/anotheralias", 1001)] - [TestCase("/page2/alias", 10011)] - [TestCase("/2ndpagealias", 10011)] - [TestCase("/only/one/alias", 100111)] - [TestCase("/ONLY/one/Alias", 100111)] - [TestCase("/alias43", 100121)] - public async Task Lookup_By_Url_Alias(string urlAsString, int nodeMatch) + // Sets up the mock domain service + protected override ServiceContext CreateServiceContext(IContentType[] contentTypes, IMediaType[] mediaTypes, IDataType[] dataTypes) { - var umbracoContext = GetUmbracoContext(urlAsString); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - var lookup = - new ContentFinderByUrlAlias(LoggerFactory.CreateLogger(), Mock.Of(), VariationContextAccessor, GetUmbracoContextAccessor(umbracoContext)); + var serviceContext = base.CreateServiceContext(contentTypes, mediaTypes, dataTypes); - var result = lookup.TryFindContent(frequest); + //setup mock domain service + var domainService = Mock.Get(serviceContext.DomainService); + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] + { + new UmbracoDomain("domain1.com/"){Id = 1, LanguageId = LangDeId, RootContentId = 1001, LanguageIsoCode = "de-DE"}, + new UmbracoDomain("domain1.com/en"){Id = 2, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("domain1.com/fr"){Id = 3, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"} + }); - Assert.IsTrue(result); - Assert.AreEqual(frequest.PublishedContent.Id, nodeMatch); + return serviceContext; } - protected override string GetXmlContent(int templateId) - { - return @" + protected virtual string GetXmlContent(int templateId) + => @" @@ -147,7 +134,12 @@ protected override string GetXmlContent(int templateId) "; - } + public const int LangDeId = 333; + public const int LangEngId = 334; + public const int LangFrId = 335; + public const int LangCzId = 336; + public const int LangNlId = 337; + public const int LangDkId = 338; } } diff --git a/tests/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlsProviderWithDomainsTests.cs similarity index 56% rename from tests/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlsProviderWithDomainsTests.cs index 4f9fdef8e0bd..28c8dd719467 100644 --- a/tests/Umbraco.Tests/Routing/UrlsProviderWithDomainsTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlsProviderWithDomainsTests.cs @@ -1,10 +1,11 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; @@ -13,77 +14,83 @@ using Umbraco.Cms.Core.Web; using Umbraco.Cms.Tests.Common; using Umbraco.Extensions; -using Umbraco.Tests.LegacyXmlPublishedCache; -namespace Umbraco.Tests.Routing +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing { [TestFixture] public class UrlsProviderWithDomainsTests : UrlRoutingTestBase { - private IUmbracoContextAccessor UmbracoContextAccessor { get; } = new TestUmbracoContextAccessor(); - protected override void Compose() + private const string CacheKeyPrefix = "NuCache.ContentCache.RouteByContent"; + + private void SetDomains1() { - base.Compose(); + var domainService = Mock.Get(DomainService); - Builder.Services.AddUnique(Mock.Of()); - Builder.Services.AddTransient(); + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] + { + new UmbracoDomain("domain1.com") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} + }); } - void SetDomains1() + private void SetDomains2() { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("domain1.com") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} - }); - } + var domainService = Mock.Get(DomainService); - void SetDomains2() - { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/foo") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} - }); + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] + { + new UmbracoDomain("http://domain1.com/foo") {Id = 1, LanguageId = LangFrId, RootContentId = 1001, LanguageIsoCode = "fr-FR"} + }); } - void SetDomains3() + private void SetDomains3() { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangFrId, RootContentId = 10011, LanguageIsoCode = "fr-FR"} - }); + var domainService = Mock.Get(DomainService); + + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] + { + new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangFrId, RootContentId = 10011, LanguageIsoCode = "fr-FR"} + }); } - void SetDomains4() + private void SetDomains4() { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain3.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} - }); + var domainService = Mock.Get(DomainService); + + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] + { + new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1.com/en") {Id = 2, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1.com/fr") {Id = 3, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain3.com/") {Id = 4, LanguageId = LangEngId, RootContentId = 1003, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/en") {Id = 5, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/fr") {Id = 6, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} + }); } - void SetDomains5() + private void SetDomains5() { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1a.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1b.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain1.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain1a.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain1b.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, - new UmbracoDomain("http://domain3.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain3.com/fr") {Id = 1, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} - }); + var domainService = Mock.Get(DomainService); + + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] + { + new UmbracoDomain("http://domain1.com/en") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1a.com/en") {Id = 2, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1b.com/en") {Id = 3, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain1.com/fr") {Id = 4, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain1a.com/fr") {Id = 5, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain1b.com/fr") {Id = 6, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"}, + new UmbracoDomain("http://domain3.com/en") {Id = 7, LanguageId = LangEngId, RootContentId = 10031, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain3.com/fr") {Id = 8, LanguageId = LangFrId, RootContentId = 10032, LanguageIsoCode = "fr-FR"} + }); } protected override string GetXmlContent(int templateId) - { - return @" + => @" @@ -162,7 +169,6 @@ protected override string GetXmlContent(int templateId) "; - } // with one simple domain "domain1.com" // basic tests @@ -179,21 +185,18 @@ protected override string GetXmlContent(int templateId) [TestCase(10011, "https://domain1.com", false, "/1001-1/")] public void Get_Url_SimpleDomain(int nodeId, string currentUrl, bool absolute, string expected) { + SetDomains1(); + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; - var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: globalSettings); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); + var umbracoContextAccessor = GetUmbracoContextAccessor("/test"); - SetDomains1(); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); var currentUri = new Uri(currentUrl); var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; - var result = publishedUrlProvider.GetUrl(nodeId, mode, current: currentUri); + var result = urlProvider.GetUrl(nodeId, mode, current: currentUri); Assert.AreEqual(expected, result); } @@ -212,21 +215,18 @@ public void Get_Url_SimpleDomain(int nodeId, string currentUrl, bool absolute, s [TestCase(10011, "https://domain1.com", false, "http://domain1.com/foo/1001-1/")] public void Get_Url_SimpleWithSchemeAndPath(int nodeId, string currentUrl, bool absolute, string expected) { + SetDomains2(); + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; - var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: globalSettings); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); + var umbracoContextAccessor = GetUmbracoContextAccessor("/test"); - SetDomains2(); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); var currentUri = new Uri(currentUrl); var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; - var result = publishedUrlProvider.GetUrl(nodeId, mode, current : currentUri); + var result = urlProvider.GetUrl(nodeId, mode, current: currentUri); Assert.AreEqual(expected, result); } @@ -237,21 +237,18 @@ public void Get_Url_SimpleWithSchemeAndPath(int nodeId, string currentUrl, bool [TestCase(1002, "http://domain1.com", false, "/1002/")] public void Get_Url_DeepDomain(int nodeId, string currentUrl, bool absolute, string expected) { + SetDomains3(); + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; - var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: globalSettings); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider(Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); + var umbracoContextAccessor = GetUmbracoContextAccessor("/test"); - SetDomains3(); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); var currentUri = new Uri(currentUrl); var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; - var result = publishedUrlProvider.GetUrl(nodeId, mode, current : currentUri); + var result = urlProvider.GetUrl(nodeId, mode, current: currentUri); Assert.AreEqual(expected, result); } @@ -268,152 +265,130 @@ public void Get_Url_DeepDomain(int nodeId, string currentUrl, bool absolute, str [TestCase(100321, "http://domain3.com", false, "/fr/1003-2-1/")] public void Get_Url_NestedDomains(int nodeId, string currentUrl, bool absolute, string expected) { + SetDomains4(); + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; - var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: globalSettings); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); + var umbracoContextAccessor = GetUmbracoContextAccessor("/test"); + + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); - SetDomains4(); var currentUri = new Uri(currentUrl); var mode = absolute ? UrlMode.Absolute : UrlMode.Auto; - var result = publishedUrlProvider.GetUrl(nodeId, mode, current : currentUri); + var result = urlProvider.GetUrl(nodeId, mode, current: currentUri); Assert.AreEqual(expected, result); } [Test] public void Get_Url_DomainsAndCache() { + SetDomains4(); + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; + + var umbracoContextAccessor = GetUmbracoContextAccessor("/test"); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); - var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: globalSettings); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - SetDomains4(); string ignore; - ignore = publishedUrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = publishedUrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = publishedUrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = publishedUrlProvider.GetUrl(10012, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = publishedUrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = publishedUrlProvider.GetUrl(10013, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = publishedUrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain1.com")); - ignore = publishedUrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain2.com")); - ignore = publishedUrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain2.com")); - ignore = publishedUrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain2.com")); - ignore = publishedUrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain2.com")); - - var cache = umbracoContext.Content as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); + ignore = urlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = urlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = urlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = urlProvider.GetUrl(10012, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = urlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = urlProvider.GetUrl(10013, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = urlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain1.com")); + ignore = urlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain2.com")); + ignore = urlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain2.com")); + ignore = urlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain2.com")); + ignore = urlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain2.com")); + + + var cache = (FastDictionaryAppCache)umbracoContext.PublishedSnapshot.ElementsCache; + var cachedRoutes = cache.Keys.Where(x => x.StartsWith(CacheKeyPrefix)).ToList(); Assert.AreEqual(7, cachedRoutes.Count); - var cachedIds = cache.RoutesCache.GetCachedIds(); - Assert.AreEqual(0, cachedIds.Count); + //var cachedIds = cache.RoutesCache.GetCachedIds(); + //Assert.AreEqual(0, cachedIds.Count); - CheckRoute(cachedRoutes, cachedIds, 1001, "1001/"); - CheckRoute(cachedRoutes, cachedIds, 10011, "10011/"); - CheckRoute(cachedRoutes, cachedIds, 100111, "10011/1001-1-1"); - CheckRoute(cachedRoutes, cachedIds, 10012, "10012/"); - CheckRoute(cachedRoutes, cachedIds, 100121, "10012/1001-2-1"); - CheckRoute(cachedRoutes, cachedIds, 10013, "1001/1001-3"); - CheckRoute(cachedRoutes, cachedIds, 1002, "/1002"); + CheckRoute(cache, 1001, "1001/"); + CheckRoute(cache, 10011, "10011/"); + CheckRoute(cache, 100111, "10011/1001-1-1"); + CheckRoute(cache, 10012, "10012/"); + CheckRoute(cache, 100121, "10012/1001-2-1"); + CheckRoute(cache, 10013, "1001/1001-3"); + CheckRoute(cache, 1002, "/1002"); // use the cache - Assert.AreEqual("/", publishedUrlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/en/", publishedUrlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/en/1001-1-1/", publishedUrlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/fr/", publishedUrlProvider.GetUrl(10012, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/fr/1001-2-1/", publishedUrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/1001-3/", publishedUrlProvider.GetUrl(10013, UrlMode.Auto, current: new Uri("http://domain1.com"))); - Assert.AreEqual("/1002/", publishedUrlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain1.com"))); - - Assert.AreEqual("http://domain1.com/fr/1001-2-1/", publishedUrlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain2.com"))); + Assert.AreEqual("/", urlProvider.GetUrl(1001, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/en/", urlProvider.GetUrl(10011, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/en/1001-1-1/", urlProvider.GetUrl(100111, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/fr/", urlProvider.GetUrl(10012, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/fr/1001-2-1/", urlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/1001-3/", urlProvider.GetUrl(10013, UrlMode.Auto, current: new Uri("http://domain1.com"))); + Assert.AreEqual("/1002/", urlProvider.GetUrl(1002, UrlMode.Auto, current: new Uri("http://domain1.com"))); + + Assert.AreEqual("http://domain1.com/fr/1001-2-1/", urlProvider.GetUrl(100121, UrlMode.Auto, current: new Uri("http://domain2.com"))); } - private static void CheckRoute(IDictionary routes, IDictionary ids, int id, string route) + private static void CheckRoute(FastDictionaryAppCache routes, int id, string route) { - Assert.IsTrue(routes.ContainsKey(id)); - Assert.AreEqual(route, routes[id]); - Assert.IsFalse(ids.ContainsKey(route)); + var cacheKey = $"{CacheKeyPrefix}[P:{id}]"; + var found = (string)routes.Get(cacheKey); + Assert.IsNotNull(found); + Assert.AreEqual(route, found); } [Test] public void Get_Url_Relative_Or_Absolute() { + SetDomains4(); + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; - var umbracoContext = GetUmbracoContext("http://domain1.com/test", 1111, globalSettings: globalSettings); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); + var umbracoContextAccessor = GetUmbracoContextAccessor("http://domain1.com/test"); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); - SetDomains4(); - Assert.AreEqual("/en/1001-1-1/", publishedUrlProvider.GetUrl(100111)); - Assert.AreEqual("http://domain3.com/en/1003-1-1/", publishedUrlProvider.GetUrl(100311)); + Assert.AreEqual("/en/1001-1-1/", urlProvider.GetUrl(100111)); + Assert.AreEqual("http://domain3.com/en/1003-1-1/", urlProvider.GetUrl(100311)); - publishedUrlProvider.Mode = UrlMode.Absolute; + urlProvider.Mode = UrlMode.Absolute; - Assert.AreEqual("http://domain1.com/en/1001-1-1/", publishedUrlProvider.GetUrl(100111)); - Assert.AreEqual("http://domain3.com/en/1003-1-1/", publishedUrlProvider.GetUrl(100311)); + Assert.AreEqual("http://domain1.com/en/1001-1-1/", urlProvider.GetUrl(100111)); + Assert.AreEqual("http://domain3.com/en/1003-1-1/", urlProvider.GetUrl(100311)); } [Test] public void Get_Url_Alternate() { + SetDomains5(); + var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; - var umbracoContext = GetUmbracoContext("http://domain1.com/en/test", 1111, globalSettings: globalSettings); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); + var umbracoContextAccessor = GetUmbracoContextAccessor("http://domain1.com/en/test"); + UrlProvider urlProvider = GetUrlProvider(umbracoContextAccessor, requestHandlerSettings, new WebRoutingSettings(), out UriUtility uriUtility); - SetDomains5(); - var url = publishedUrlProvider.GetUrl(100111, UrlMode.Absolute); + var url = urlProvider.GetUrl(100111, UrlMode.Absolute); Assert.AreEqual("http://domain1.com/en/1001-1-1/", url); - var result = publishedUrlProvider.GetOtherUrls(100111).ToArray(); + var result = urlProvider.GetOtherUrls(100111).ToArray(); - foreach (var x in result) Console.WriteLine(x); + foreach (var x in result) + Console.WriteLine(x); Assert.AreEqual(2, result.Length); Assert.AreEqual(result[0].Text, "http://domain1b.com/en/1001-1-1/"); Assert.AreEqual(result[1].Text, "http://domain1a.com/en/1001-1-1/"); } - private IPublishedUrlProvider GetPublishedUrlProvider(IUmbracoContext umbracoContext, DefaultUrlProvider urlProvider) - { - var webRoutingSettings = new WebRoutingSettings(); - return new UrlProvider( - new TestUmbracoContextAccessor(umbracoContext), - Microsoft.Extensions.Options.Options.Create(webRoutingSettings), - new UrlProviderCollection(new []{urlProvider}), - new MediaUrlProviderCollection(Enumerable.Empty()), - Mock.Of() - ); - } } } diff --git a/tests/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlsWithNestedDomains.cs similarity index 85% rename from tests/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlsWithNestedDomains.cs index ab9058bc9cc7..5e4d7f104e15 100644 --- a/tests/Umbraco.Tests/Routing/UrlsWithNestedDomains.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Core/Routing/UrlsWithNestedDomains.cs @@ -5,17 +5,19 @@ using Microsoft.Extensions.Logging; using Moq; using NUnit.Framework; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Web; using Umbraco.Cms.Tests.Common; using Umbraco.Extensions; -using Umbraco.Tests.LegacyXmlPublishedCache; -namespace Umbraco.Tests.Routing +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Routing { [TestFixture] public class UrlsWithNestedDomains : UrlRoutingTestBase @@ -25,61 +27,60 @@ public class UrlsWithNestedDomains : UrlRoutingTestBase // using the closest domain to the node - here we test that if we request // a non-canonical route, it is not cached / the cache is not polluted - protected override void Compose() - { - base.Compose(); - Builder.Services.AddUnique(Mock.Of()); - Builder.Services.AddTransient(); - } - [Test] public async Task DoNotPolluteCache() { var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; SetDomains1(); const string url = "http://domain1.com/1001-1/1001-1-1"; // get the nice URL for 100111 - var umbracoContext = GetUmbracoContext(url, 9999, globalSettings: globalSettings); - var umbracoContextAccessor = GetUmbracoContextAccessor(umbracoContext); + var umbracoContextAccessor = GetUmbracoContextAccessor(url); + var umbracoContext = umbracoContextAccessor.GetRequiredUmbracoContext(); + var urlProvider = new DefaultUrlProvider( Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); + Mock.Of>(), + new SiteDomainMapper(), + umbracoContextAccessor, + new UriUtility(Mock.Of()), + Mock.Of()); var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - Assert.AreEqual("http://domain2.com/1001-1-1/", publishedUrlProvider.GetUrl(100111, UrlMode.Absolute)); + string absUrl = publishedUrlProvider.GetUrl(100111, UrlMode.Absolute); + Assert.AreEqual("http://domain2.com/1001-1-1/", absUrl); + + const string cacheKeyPrefix = "NuCache.ContentCache.RouteByContent"; // check that the proper route has been cached - var cache = umbracoContext.Content as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); + var cache = (FastDictionaryAppCache)umbracoContext.PublishedSnapshot.ElementsCache; + + var cachedRoutes = cache.Keys.Where(x => x.StartsWith(cacheKeyPrefix)).ToList(); + var cacheKey = $"{cacheKeyPrefix}[P:100111]"; + Assert.AreEqual("10011/1001-1-1", cache.Get(cacheKey)); // route a rogue URL var publishedRouter = CreatePublishedRouter(umbracoContextAccessor); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); + var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); publishedRouter.FindDomain(frequest); Assert.IsTrue(frequest.HasDomain()); // check that it's been routed - var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); + var lookup = new ContentFinderByUrl(Mock.Of>(), umbracoContextAccessor); var result = lookup.TryFindContent(frequest); Assert.IsTrue(result); Assert.AreEqual(100111, frequest.PublishedContent.Id); // has the cache been polluted? - cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual("10011/1001-1-1", cachedRoutes[100111]); // no - //Assert.AreEqual("1001/1001-1/1001-1-1", cachedRoutes[100111]); // yes + cachedRoutes = cache.Keys.Where(x => x.StartsWith(cacheKeyPrefix)).ToList(); + Assert.AreEqual("10011/1001-1-1", cache.Get(cacheKey)); // no // what's the nice URL now? Assert.AreEqual("http://domain2.com/1001-1-1/", publishedUrlProvider.GetUrl(100111)); // good - //Assert.AreEqual("http://domain1.com/1001-1/1001-1-1", routingContext.NiceUrlProvider.GetNiceUrl(100111, true)); // bad } private IPublishedUrlProvider GetPublishedUrlProvider(IUmbracoContext umbracoContext, object urlProvider) @@ -87,14 +88,16 @@ private IPublishedUrlProvider GetPublishedUrlProvider(IUmbracoContext umbracoCon throw new NotImplementedException(); } - void SetDomains1() + private void SetDomains1() { - SetupDomainServiceMock(new[] - { - new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, - new UmbracoDomain("http://domain2.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"} - }); - + var domainService = Mock.Get(DomainService); + + domainService.Setup(service => service.GetAll(It.IsAny())) + .Returns((bool incWildcards) => new[] + { + new UmbracoDomain("http://domain1.com/") {Id = 1, LanguageId = LangEngId, RootContentId = 1001, LanguageIsoCode = "en-US"}, + new UmbracoDomain("http://domain2.com/") {Id = 2, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"} + }); } private IPublishedUrlProvider GetPublishedUrlProvider(IUmbracoContext umbracoContext, DefaultUrlProvider urlProvider) @@ -103,15 +106,14 @@ private IPublishedUrlProvider GetPublishedUrlProvider(IUmbracoContext umbracoCon return new UrlProvider( new TestUmbracoContextAccessor(umbracoContext), Microsoft.Extensions.Options.Options.Create(webRoutingSettings), - new UrlProviderCollection(new []{urlProvider}), - new MediaUrlProviderCollection(Enumerable.Empty()), + new UrlProviderCollection(() => new[] { urlProvider }), + new MediaUrlProviderCollection(() => Enumerable.Empty()), Mock.Of() ); } protected override string GetXmlContent(int templateId) - { - return @" + => @" @@ -190,6 +192,5 @@ protected override string GetXmlContent(int templateId) "; - } } } diff --git a/tests/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/ContentSerializationTests.cs similarity index 84% rename from tests/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/ContentSerializationTests.cs index 9a44cf35f960..46824d6386ff 100644 --- a/tests/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/ContentSerializationTests.cs @@ -1,12 +1,12 @@ -using Moq; +using Moq; using NUnit.Framework; using System; using System.Collections.Generic; -using Umbraco.Core.Models; -using Umbraco.Core.PropertyEditors; -using Umbraco.Web.PublishedCache.NuCache.DataSource; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; -namespace Umbraco.Tests.PublishedContent +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache { [TestFixture] public class ContentSerializationTests @@ -77,9 +77,12 @@ public class CultureVariationComparer : Comparer { public override int Compare(CultureVariation x, CultureVariation y) { - if (x == null && y == null) return 0; - if (x == null && y != null) return -1; - if (x != null && y == null) return 1; + if (x == null && y == null) + return 0; + if (x == null && y != null) + return -1; + if (x != null && y == null) + return 1; return x.Date.CompareTo(y.Date) | x.IsDraft.CompareTo(y.IsDraft) | x.Name.CompareTo(y.Name) | x.UrlSegment.CompareTo(y.UrlSegment); } @@ -89,9 +92,12 @@ public class PropertyDataComparer : Comparer { public override int Compare(PropertyData x, PropertyData y) { - if (x == null && y == null) return 0; - if (x == null && y != null) return -1; - if (x != null && y == null) return 1; + if (x == null && y == null) + return 0; + if (x == null && y != null) + return -1; + if (x != null && y == null) + return 1; var xVal = x.Value?.ToString() ?? string.Empty; var yVal = y.Value?.ToString() ?? string.Empty; diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentCacheTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentCacheTests.cs new file mode 100644 index 000000000000..0ef8a856fdf2 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentCacheTests.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache +{ + [TestFixture] + public class PublishContentCacheTests : PublishedSnapshotServiceTestBase + { + private IPublishedContentCache _cache; + + [SetUp] + public override void Setup() + { + base.Setup(); + + string xml = PublishedContentXml.PublishContentCacheTestsXml(); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + // configure the Home content type to be composed of another for tests. + var compositionType = new ContentType(TestHelper.ShortStringHelper, -1) + { + Alias = "MyCompositionAlias" + }; + contentTypes.First(x => x.Alias == "Home").AddContentType(compositionType); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + _cache = GetPublishedSnapshot().Content; + } + + [Test] + public void Has_Content() + { + Assert.IsTrue(_cache.HasContent()); + } + + + [Test] + public void Get_Root_Docs() + { + var result = _cache.GetAtRoot(); + Assert.AreEqual(2, result.Count()); + Assert.AreEqual(1046, result.ElementAt(0).Id); + Assert.AreEqual(1172, result.ElementAt(1).Id); + } + + + [TestCase("/", 1046)] + [TestCase("/home", 1046)] + [TestCase("/Home", 1046)] //test different cases + [TestCase("/home/sub1", 1173)] + [TestCase("/Home/sub1", 1173)] + [TestCase("/home/Sub1", 1173)] //test different cases + [TestCase("/home/Sub'Apostrophe", 1177)] + public void Get_Node_By_Route(string route, int nodeId) + { + var result = _cache.GetByRoute(route, false); + Assert.IsNotNull(result); + Assert.AreEqual(nodeId, result.Id); + } + + + + [TestCase("/", 1046)] + [TestCase("/sub1", 1173)] + [TestCase("/Sub1", 1173)] + public void Get_Node_By_Route_Hiding_Top_Level_Nodes(string route, int nodeId) + { + var result = _cache.GetByRoute(route, true); + Assert.IsNotNull(result); + Assert.AreEqual(nodeId, result.Id); + } + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentDataTableTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentDataTableTests.cs new file mode 100644 index 000000000000..80920e071fdc --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentDataTableTests.cs @@ -0,0 +1,200 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Routing; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; +using Umbraco.Cms.Tests.Common.Builders; +using Umbraco.Cms.Tests.Common.Builders.Extensions; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache +{ + /// + /// Unit tests for IPublishedContent and extensions + /// + [TestFixture] + public class PublishedContentDataTableTests : PublishedSnapshotServiceTestBase + { + private readonly DataType[] _dataTypes = GetDefaultDataTypes(); + + private static ContentType CreateContentType(string name, IDataType dataType, IReadOnlyDictionary propertyAliasesAndNames) + { + var contentType = new ContentType(TestHelper.ShortStringHelper, -1) + { + Alias = name, + Name = name, + Key = Guid.NewGuid(), + Id = name.GetHashCode() + }; + foreach(var prop in propertyAliasesAndNames) + { + contentType.AddPropertyType(new PropertyType(TestHelper.ShortStringHelper, dataType, prop.Key) + { + Name = prop.Value + }); + } + + return contentType; + } + + private IEnumerable CreateCache( + bool createChildren, + IDataType dataType, + out ContentType[] contentTypes) + { + var result = new List(); + var valueCounter = 1; + var parentId = 3; + + var properties = new Dictionary + { + ["property1"] = "Property 1", + ["property2"] = "Property 2", + }; + + ContentType parentContentType = CreateContentType("Parent", dataType, new Dictionary(properties) + { + ["property3"] = "Property 3" + }); + ContentType childContentType = CreateContentType("Child", dataType, new Dictionary(properties) + { + ["property4"] = "Property 4" + }); + ContentType child2ContentType = CreateContentType("Child2", dataType, new Dictionary(properties) + { + ["property4"] = "Property 4" + }); + + contentTypes = new[] { parentContentType, childContentType, child2ContentType }; + + ContentData parentData = new ContentDataBuilder() + .WithName("Page" + Guid.NewGuid()) + .WithProperties(new PropertyDataBuilder() + .WithPropertyData("property1", "value" + valueCounter) + .WithPropertyData("property2", "value" + (valueCounter + 1)) + .WithPropertyData("property3", "value" + (valueCounter + 2)) + .Build()) + .Build(); + + ContentNodeKit parent = ContentNodeKitBuilder.CreateWithContent( + parentContentType.Id, + parentId, $"-1,{parentId}", + draftData: parentData, + publishedData: parentData); + + result.Add(parent); + + if (createChildren) + { + for (int i = 0; i < 3; i++) + { + valueCounter += 3; + var childId = parentId + i + 1; + + ContentData childData = new ContentDataBuilder() + .WithName("Page" + Guid.NewGuid()) + .WithProperties(new PropertyDataBuilder() + .WithPropertyData("property1", "value" + valueCounter) + .WithPropertyData("property2", "value" + (valueCounter + 1)) + .WithPropertyData("property4", "value" + (valueCounter + 2)) + .Build()) + .Build(); + + ContentNodeKit child = ContentNodeKitBuilder.CreateWithContent( + i > 0 ? childContentType.Id : child2ContentType.Id, + childId, $"-1,{parentId},{childId}", i, + draftData: childData, + publishedData: childData); + + result.Add(child); + } + } + + return result; + } + + [Test] + public void To_DataTable() + { + var cache = CreateCache(true, _dataTypes[0], out ContentType[] contentTypes); + InitializedCache(cache, contentTypes, dataTypes: _dataTypes); + + var snapshot = GetPublishedSnapshot(); + var root = snapshot.Content.GetAtRoot().First(); + + var dt = root.ChildrenAsTable( + VariationContextAccessor, + ContentTypeService, + MediaTypeService, + Mock.Of(), + Mock.Of()); + + Assert.AreEqual(11, dt.Columns.Count); + Assert.AreEqual(3, dt.Rows.Count); + Assert.AreEqual("value4", dt.Rows[0]["Property 1"]); + Assert.AreEqual("value5", dt.Rows[0]["Property 2"]); + Assert.AreEqual("value6", dt.Rows[0]["Property 4"]); + Assert.AreEqual("value7", dt.Rows[1]["Property 1"]); + Assert.AreEqual("value8", dt.Rows[1]["Property 2"]); + Assert.AreEqual("value9", dt.Rows[1]["Property 4"]); + Assert.AreEqual("value10", dt.Rows[2]["Property 1"]); + Assert.AreEqual("value11", dt.Rows[2]["Property 2"]); + Assert.AreEqual("value12", dt.Rows[2]["Property 4"]); + } + + [Test] + public void To_DataTable_With_Filter() + { + var cache = CreateCache(true, _dataTypes[0], out ContentType[] contentTypes); + InitializedCache(cache, contentTypes, dataTypes: _dataTypes); + + var snapshot = GetPublishedSnapshot(); + var root = snapshot.Content.GetAtRoot().First(); + + var dt = root.ChildrenAsTable( + VariationContextAccessor, + ContentTypeService, + MediaTypeService, + Mock.Of(), + Mock.Of(), + "Child"); + + Assert.AreEqual(11, dt.Columns.Count); + Assert.AreEqual(2, dt.Rows.Count); + Assert.AreEqual("value7", dt.Rows[0]["Property 1"]); + Assert.AreEqual("value8", dt.Rows[0]["Property 2"]); + Assert.AreEqual("value9", dt.Rows[0]["Property 4"]); + Assert.AreEqual("value10", dt.Rows[1]["Property 1"]); + Assert.AreEqual("value11", dt.Rows[1]["Property 2"]); + Assert.AreEqual("value12", dt.Rows[1]["Property 4"]); + } + + [Test] + public void To_DataTable_No_Rows() + { + var cache = CreateCache(false, _dataTypes[0], out ContentType[] contentTypes); + InitializedCache(cache, contentTypes, dataTypes: _dataTypes); + + var snapshot = GetPublishedSnapshot(); + var root = snapshot.Content.GetAtRoot().First(); + + var dt = root.ChildrenAsTable( + VariationContextAccessor, + ContentTypeService, + MediaTypeService, + Mock.Of(), + Mock.Of()); + + //will return an empty data table + Assert.AreEqual(0, dt.Columns.Count); + Assert.AreEqual(0, dt.Rows.Count); + } + + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentExtensionTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentExtensionTests.cs new file mode 100644 index 000000000000..c3af93f9111d --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentExtensionTests.cs @@ -0,0 +1,77 @@ +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache +{ + [TestFixture] + public class PublishedContentExtensionTests : PublishedSnapshotServiceTestBase + { + private const string XmlContent = @" + + +]> + + +"; + + [SetUp] + public override void Setup() + { + base.Setup(); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + XmlContent, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + // configure inheritance for content types + var baseType = new ContentType(TestHelper.ShortStringHelper, -1) { Alias = "Base" }; + contentTypes[0].AddContentType(baseType); + + InitializedCache(kits, contentTypes, dataTypes); + } + + [Test] + public void IsDocumentType_NonRecursive_ActualType_ReturnsTrue() + { + var publishedContent = GetContent(1100); + Assert.That(publishedContent.IsDocumentType("Inherited", false)); + } + + [Test] + public void IsDocumentType_NonRecursive_BaseType_ReturnsFalse() + { + var publishedContent = GetContent(1100); + Assert.That(publishedContent.IsDocumentType("Base", false), Is.False); + } + + [Test] + public void IsDocumentType_Recursive_ActualType_ReturnsTrue() + { + var publishedContent = GetContent(1100); + Assert.That(publishedContent.IsDocumentType("Inherited", true)); + } + + [Test] + public void IsDocumentType_Recursive_BaseType_ReturnsTrue() + { + var publishedContent = GetContent(1100); + Assert.That(publishedContent.IsDocumentType("Base", true)); + } + + [Test] + public void IsDocumentType_Recursive_InvalidBaseType_ReturnsFalse() + { + var publishedContent = GetContent(1100); + Assert.That(publishedContent.IsDocumentType("invalidbase", true), Is.False); + } + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs new file mode 100644 index 000000000000..3098dc6752f1 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs @@ -0,0 +1,346 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.PropertyEditors.ValueConverters; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; +using Umbraco.Cms.Tests.Common.Builders; +using Umbraco.Cms.Tests.Common.Builders.Extensions; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache +{ + [TestFixture] + public class PublishedContentLanguageVariantTests : PublishedSnapshotServiceTestBase + { + [SetUp] + public override void Setup() + { + base.Setup(); + + var dataTypes = GetDefaultDataTypes(); + var cache = CreateCache(dataTypes, out ContentType[] contentTypes); + + InitializedCache(cache, contentTypes, dataTypes: dataTypes); + } + + protected override PropertyValueConverterCollection PropertyValueConverterCollection + { + get + { + PropertyValueConverterCollection collection = base.PropertyValueConverterCollection; + return new PropertyValueConverterCollection(() => collection.Append(new TestNoValueValueConverter())); + } + } + + private class TestNoValueValueConverter : SimpleTinyMceValueConverter + { + public override bool IsConverter(IPublishedPropertyType propertyType) + => propertyType.Alias == "noprop"; + + // for this test, we return false for IsValue for this property + public override bool? IsValue(object value, PropertyValueLevel level) => false; + } + + /// + /// Override to mock localization service + /// + /// + /// + /// + protected override ServiceContext CreateServiceContext(IContentType[] contentTypes, IMediaType[] mediaTypes, IDataType[] dataTypes) + { + var serviceContext = base.CreateServiceContext(contentTypes, mediaTypes, dataTypes); + + var localizationService = Mock.Get(serviceContext.LocalizationService); + + var languages = new List + { + new Language(GlobalSettings, "en-US") { Id = 1, CultureName = "English", IsDefault = true }, + new Language(GlobalSettings, "fr") { Id = 2, CultureName = "French" }, + new Language(GlobalSettings, "es") { Id = 3, CultureName = "Spanish", FallbackLanguageId = 1 }, + new Language(GlobalSettings, "it") { Id = 4, CultureName = "Italian", FallbackLanguageId = 3 }, + new Language(GlobalSettings, "de") { Id = 5, CultureName = "German" }, + new Language(GlobalSettings, "da") { Id = 6, CultureName = "Danish", FallbackLanguageId = 8 }, + new Language(GlobalSettings, "sv") { Id = 7, CultureName = "Swedish", FallbackLanguageId = 6 }, + new Language(GlobalSettings, "no") { Id = 8, CultureName = "Norweigan", FallbackLanguageId = 7 }, + new Language(GlobalSettings, "nl") { Id = 9, CultureName = "Dutch", FallbackLanguageId = 1 } + }; + + localizationService.Setup(x => x.GetAllLanguages()).Returns(languages); + localizationService.Setup(x => x.GetLanguageById(It.IsAny())) + .Returns((int id) => languages.SingleOrDefault(y => y.Id == id)); + localizationService.Setup(x => x.GetLanguageByIsoCode(It.IsAny())) + .Returns((string c) => languages.SingleOrDefault(y => y.IsoCode == c)); + + return serviceContext; + } + + /// + /// Creates a content cache + /// + /// + /// + /// + /// + /// Builds a content hierarchy of 3 nodes, each has a different set of cultural properties. + /// The first 2 share the same content type, the last one is a different content type. + /// NOTE: The content items themselves are 'Invariant' but their properties are 'Variant' by culture. + /// Normally in Umbraco this is prohibited but our APIs and database do actually support that behavior. + /// It is simpler to have these tests run this way, else we would need to use WithCultureInfos + /// for each item and pass in name values for all cultures we are supporting and then specify the + /// default VariationContextAccessor.VariationContext value to be a default culture instead of "". + /// + private IEnumerable CreateCache(IDataType[] dataTypes, out ContentType[] contentTypes) + { + var result = new List(); + + var propertyDataTypes = new Dictionary + { + // we only have one data type for this test which will be resolved with string empty. + [string.Empty] = dataTypes[0] + }; + + var contentType1 = new ContentType(ShortStringHelper, -1); + + ContentData item1Data = new ContentDataBuilder() + .WithName("Content 1") + .WithProperties(new PropertyDataBuilder() + .WithPropertyData("welcomeText", "Welcome") + .WithPropertyData("welcomeText", "Welcome", "en-US") + .WithPropertyData("welcomeText", "Willkommen", "de") + .WithPropertyData("welcomeText", "Welkom", "nl") + .WithPropertyData("welcomeText2", "Welcome") + .WithPropertyData("welcomeText2", "Welcome", "en-US") + .WithPropertyData("noprop", "xxx") + .Build()) + // build with a dynamically created content type + .Build(ShortStringHelper, propertyDataTypes, contentType1, "ContentType1"); + + ContentNodeKit item1 = ContentNodeKitBuilder.CreateWithContent( + contentType1.Id, + 1, "-1,1", + draftData: item1Data, + publishedData: item1Data); + + result.Add(item1); + + ContentData item2Data = new ContentDataBuilder() + .WithName("Content 2") + .WithProperties(new PropertyDataBuilder() + .WithPropertyData("welcomeText", "Welcome") + .WithPropertyData("welcomeText", "Welcome", "en-US") + .WithPropertyData("noprop", "xxx") + .Build()) + // build while dynamically updating the same content type + .Build(ShortStringHelper, propertyDataTypes, contentType1); + + ContentNodeKit item2 = ContentNodeKitBuilder.CreateWithContent( + contentType1.Id, + 2, "-1,1,2", + parentContentId: 1, + draftData: item2Data, + publishedData: item2Data); + + result.Add(item2); + + var contentType2 = new ContentType(ShortStringHelper, -1); + + ContentData item3Data = new ContentDataBuilder() + .WithName("Content 3") + .WithProperties(new PropertyDataBuilder() + .WithPropertyData("prop3", "Oxxo") + .WithPropertyData("prop3", "Oxxo", "en-US") + .Build()) + // build with a dynamically created content type + .Build(ShortStringHelper, propertyDataTypes, contentType2, "ContentType2"); + + ContentNodeKit item3 = ContentNodeKitBuilder.CreateWithContent( + contentType2.Id, + 3, "-1,1,2,3", + parentContentId: 2, + draftData: item3Data, + publishedData: item3Data); + + result.Add(item3); + + contentTypes = new[] { contentType1, contentType2 }; + + return result; + } + + [Test] + public void Can_Get_Content_For_Populated_Requested_Language() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First(); + var value = content.Value(Mock.Of(), "welcomeText", "en-US"); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Can_Get_Content_For_Populated_Requested_Non_Default_Language() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First(); + var value = content.Value(Mock.Of(), "welcomeText", "de"); + Assert.AreEqual("Willkommen", value); + } + + [Test] + public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_Without_Fallback() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First(); + var value = content.Value(Mock.Of(), "welcomeText", "fr"); + Assert.IsNull(value); + } + + [Test] + public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Unless_Requested() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First(); + var value = content.Value(Mock.Of(), "welcomeText", "es"); + Assert.IsNull(value); + } + + [Test] + public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First(); + var value = content.Value(PublishedValueFallback, "welcomeText", "es", fallback: Fallback.ToLanguage); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Over_Two_Levels() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First(); + var value = content.Value(PublishedValueFallback, "welcomeText", "it", fallback: Fallback.To(Fallback.Language, Fallback.Ancestors)); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_Over_That_Loops() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First(); + var value = content.Value(Mock.Of(), "welcomeText", "no", fallback: Fallback.ToLanguage); + Assert.IsNull(value); + } + + [Test] + public void Do_Not_Get_Content_Recursively_Unless_Requested() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First().Children.First(); + var value = content.Value(Mock.Of(), "welcomeText2"); + Assert.IsNull(value); + } + + [Test] + public void Can_Get_Content_Recursively() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First().Children.First(); + var value = content.Value(PublishedValueFallback, "welcomeText2", fallback: Fallback.ToAncestors); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Do_Not_Get_Content_Recursively_Unless_Requested2() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First().Children.First().Children.First(); + Assert.IsNull(content.GetProperty("welcomeText2")); + var value = content.Value(Mock.Of(), "welcomeText2"); + Assert.IsNull(value); + } + + [Test] + public void Can_Get_Content_Recursively2() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First().Children.First().Children.First(); + Assert.IsNull(content.GetProperty("welcomeText2")); + var value = content.Value(PublishedValueFallback, "welcomeText2", fallback: Fallback.ToAncestors); + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Can_Get_Content_Recursively3() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First().Children.First().Children.First(); + Assert.IsNull(content.GetProperty("noprop")); + var value = content.Value(PublishedValueFallback, "noprop", fallback: Fallback.ToAncestors); + // property has no value - based on the converter + // but we still get the value (ie, the converter would do something) + Assert.AreEqual("xxx", value.ToString()); + } + + [Test] + public void Can_Get_Content_With_Recursive_Priority() + { + VariationContextAccessor.VariationContext = new VariationContext("nl"); + + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First().Children.First(); + + var value = content.Value(PublishedValueFallback, "welcomeText", "nl", fallback: Fallback.To(Fallback.Ancestors, Fallback.Language)); + + // No Dutch value is directly assigned. Check has fallen back to Dutch value from parent. + Assert.AreEqual("Welkom", value); + } + + [Test] + public void Can_Get_Content_With_Fallback_Language_Priority() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First().Children.First(); + + var value = content.Value(PublishedValueFallback, "welcomeText", "nl", fallback: Fallback.ToLanguage); + + // No Dutch value is directly assigned. Check has fallen back to English value from language variant. + Assert.AreEqual("Welcome", value); + } + + [Test] + public void Throws_For_Non_Supported_Fallback() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First().Children.First(); + + Assert.Throws(() => content.Value(PublishedValueFallback, "welcomeText", "nl", fallback: Fallback.To(999))); + } + + [Test] + public void Can_Fallback_To_Default_Value() + { + var snapshot = GetPublishedSnapshot(); + var content = snapshot.Content.GetAtRoot().First().Children.First(); + + // no Dutch value is assigned, so getting null + var value = content.Value(PublishedValueFallback, "welcomeText", "nl"); + Assert.IsNull(value); + + // even if we 'just' provide a default value + value = content.Value(PublishedValueFallback, "welcomeText", "nl", defaultValue: "woop"); + Assert.IsNull(value); + + // but it works with proper fallback settings + value = content.Value(PublishedValueFallback, "welcomeText", "nl", fallback: Fallback.ToDefaultValue, defaultValue: "woop"); + Assert.AreEqual("woop", value); + } + } +} diff --git a/tests/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentTests.cs similarity index 51% rename from tests/Umbraco.Tests/PublishedContent/PublishedContentTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentTests.cs index c34d9e7595b3..d7a5a2bc4892 100644 --- a/tests/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentTests.cs @@ -1,193 +1,108 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; using Moq; using NUnit.Framework; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Core.Templates; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Serialization; -using Umbraco.Cms.Tests.Common.Testing; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.Composing; -namespace Umbraco.Tests.PublishedContent +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache { - /// - /// Tests the methods on IPublishedContent using the DefaultPublishedContentStore - /// + [TestFixture] - [UmbracoTest(TypeLoader = UmbracoTestOptions.TypeLoader.PerFixture)] - public class PublishedContentTests : PublishedContentTestBase + public class PublishedContentTests : PublishedSnapshotServiceTestBase { - protected override void Compose() - { - base.Compose(); - _publishedSnapshotAccessorMock = new Mock(); - Builder.Services.AddUnique(_publishedSnapshotAccessorMock.Object); - - Builder.Services.AddUnique(f => new PublishedModelFactory(f.GetRequiredService().GetTypes(), f.GetRequiredService())); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - - var loggerFactory = NullLoggerFactory.Instance; - var mediaService = Mock.Of(); - var contentTypeBaseServiceProvider = Mock.Of(); - var umbracoContextAccessor = Mock.Of(); - var backOfficeSecurityAccessor = Mock.Of(); - var publishedUrlProvider = Mock.Of(); - var imageSourceParser = new HtmlImageSourceParser(publishedUrlProvider); - var serializer = new ConfigurationEditorJsonSerializer(); - var mediaFileService = new MediaFileManager(Mock.Of(), Mock.Of(), - loggerFactory.CreateLogger(), Mock.Of()); - var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), HostingEnvironment, mediaService, contentTypeBaseServiceProvider, mediaFileService, ShortStringHelper, publishedUrlProvider, serializer); - var linkParser = new HtmlLocalLinkParser(umbracoContextAccessor, publishedUrlProvider); - - var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new VoidEditor(DataValueEditorFactory), serializer) { Id = 1 }, - new DataType(new TrueFalsePropertyEditor(DataValueEditorFactory, IOHelper), serializer) { Id = 1001 }, - new DataType(new RichTextPropertyEditor(DataValueEditorFactory, backOfficeSecurityAccessor, imageSourceParser, linkParser, pastedImages, IOHelper, Mock.Of()), serializer) { Id = 1002 }, - new DataType(new IntegerPropertyEditor(DataValueEditorFactory), serializer) { Id = 1003 }, - new DataType(new TextboxPropertyEditor(DataValueEditorFactory, IOHelper), serializer) { Id = 1004 }, - new DataType(new MediaPickerPropertyEditor(DataValueEditorFactory, IOHelper), serializer) { Id = 1005 }); - Builder.Services.AddUnique(f => dataTypeService); - } - - protected override void Initialize() - { - base.Initialize(); - - var factory = Factory.GetRequiredService() as PublishedContentTypeFactory; - - // need to specify a custom callback for unit tests - // AutoPublishedContentTypes generates properties automatically - // when they are requested, but we must declare those that we - // explicitely want to be here... + private readonly Guid _node1173Guid = Guid.NewGuid(); + private PublishedModelFactory _publishedModelFactory; + private DataType[] _dataTypes; - IEnumerable CreatePropertyTypes(IPublishedContentType contentType) + [SetUp] + public override void Setup() + { + base.Setup(); + + string xml = PublishedContentXml.PublishedContentTestXml(1234, _node1173Guid); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + _dataTypes = dataTypes; + + // configure the Home content type to be composed of another for tests. + var compositionType = new ContentType(TestHelper.ShortStringHelper, -1) { - // AutoPublishedContentType will auto-generate other properties - yield return factory.CreatePropertyType(contentType, "umbracoNaviHide", 1001); - yield return factory.CreatePropertyType(contentType, "selectedNodes", 1); - yield return factory.CreatePropertyType(contentType, "umbracoUrlAlias", 1); - yield return factory.CreatePropertyType(contentType, "content", 1002); - yield return factory.CreatePropertyType(contentType, "testRecursive", 1); - } + Alias = "MyCompositionAlias" + }; + contentTypes.First(x => x.Alias == "Home").AddContentType(compositionType); - var compositionAliases = new[] { "MyCompositionAlias" }; - var anythingType = new AutoPublishedContentType(Guid.NewGuid(), 0, "anything", compositionAliases, CreatePropertyTypes); - var homeType = new AutoPublishedContentType(Guid.NewGuid(), 0, "home", compositionAliases, CreatePropertyTypes); - ContentTypesCache.GetPublishedContentTypeByAlias = alias => alias.InvariantEquals("home") ? homeType : anythingType; + InitializedCache(kits, contentTypes, dataTypes: dataTypes); } + // override to specify our own factory with custom types + protected override IPublishedModelFactory PublishedModelFactory + => _publishedModelFactory ??= new PublishedModelFactory( + new[] { typeof(Home), typeof(Anything), typeof(CustomDocument) }, + PublishedValueFallback); - protected override TypeLoader CreateTypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, ILogger logger, IProfilingLogger profilingLogger , IHostingEnvironment hostingEnvironment) + [PublishedModel("Home")] + internal class Home : PublishedContentModel { - var baseLoader = base.CreateTypeLoader(ioHelper, typeFinder, runtimeCache, logger, profilingLogger , hostingEnvironment); + public Home(IPublishedContent content, IPublishedValueFallback fallback) + : base(content, fallback) + { } - return new TypeLoader(typeFinder, runtimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), logger, profilingLogger , false, - // this is so the model factory looks into the test assembly - baseLoader.AssembliesToScan - .Union(new[] { typeof(PublishedContentTests).Assembly }) - .ToList()); + public bool UmbracoNaviHide => this.Value(Mock.Of(), "umbracoNaviHide"); } - private readonly Guid _node1173Guid = Guid.NewGuid(); - private Mock _publishedSnapshotAccessorMock; - - protected override string GetXmlContent(int templateId) - { - return @" - - - - -]> - - - - - 1 - - - This is some content]]> - - - - - - - - - - - - - - - - 1 - - - - - - - - - -"; - } - - internal IPublishedContent GetNode(int id) - { - var ctx = GetUmbracoContext("/test"); - var doc = ctx.Content.GetById(id); - Assert.IsNotNull(doc); - return doc; + [PublishedModel("anything")] + internal class Anything : PublishedContentModel + { + public Anything(IPublishedContent content, IPublishedValueFallback fallback) + : base(content, fallback) + { } + } + + [PublishedModel("CustomDocument")] + internal class CustomDocument : PublishedContentModel + { + public CustomDocument(IPublishedContent content, IPublishedValueFallback fallback) + : base(content, fallback) + { } } [Test] public void GetNodeByIds() { - var ctx = GetUmbracoContext("/test"); - var contentById = ctx.Content.GetById(1173); + var snapshot = GetPublishedSnapshot(); + + var contentById = snapshot.Content.GetById(1173); Assert.IsNotNull(contentById); - var contentByGuid = ctx.Content.GetById(_node1173Guid); + var contentByGuid = snapshot.Content.GetById(_node1173Guid); Assert.IsNotNull(contentByGuid); Assert.AreEqual(contentById.Id, contentByGuid.Id); Assert.AreEqual(contentById.Key, contentByGuid.Key); - contentById = ctx.Content.GetById(666); + contentById = snapshot.Content.GetById(666); Assert.IsNull(contentById); - contentByGuid = ctx.Content.GetById(Guid.NewGuid()); + contentByGuid = snapshot.Content.GetById(Guid.NewGuid()); Assert.IsNull(contentByGuid); } [Test] public void Is_Last_From_Where_Filter_Dynamic_Linq() { - var doc = GetNode(1173); + var doc = GetContent(1173); var items = doc.Children(VariationContextAccessor).Where(x => x.IsVisible(Mock.Of())).ToIndexedArray(); @@ -195,11 +110,11 @@ public void Is_Last_From_Where_Filter_Dynamic_Linq() { if (item.Content.Id != 1178) { - Assert.IsFalse(item.IsLast()); + Assert.IsFalse(item.IsLast(), $"The item {item.Content.Id} is last"); } else { - Assert.IsTrue(item.IsLast()); + Assert.IsTrue(item.IsLast(), $"The item {item.Content.Id} is not last"); } } } @@ -207,7 +122,7 @@ public void Is_Last_From_Where_Filter_Dynamic_Linq() [Test] public void Is_Last_From_Where_Filter() { - var doc = GetNode(1173); + var doc = GetContent(1173); var items = doc .Children(VariationContextAccessor) @@ -243,30 +158,14 @@ public void Is_Last_From_Where_Filter() } } - [PublishedModel("Home")] - internal class Home : PublishedContentModel - { - public Home(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - {} - } - - [PublishedModel("anything")] - internal class Anything : PublishedContentModel - { - public Anything(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - { } - } - [Test] public void Is_Last_From_Where_Filter2() { - var doc = GetNode(1173); + var doc = GetContent(1173); var ct = doc.ContentType; var items = doc.Children(VariationContextAccessor) - .Select(x => x.CreateModel(Current.PublishedModelFactory)) // linq, returns IEnumerable + .Select(x => x.CreateModel(PublishedModelFactory)) // linq, returns IEnumerable // only way around this is to make sure every IEnumerable extension // explicitely returns a PublishedContentSet, not an IEnumerable @@ -295,7 +194,7 @@ public void Is_Last_From_Where_Filter2() [Test] public void Is_Last_From_Take() { - var doc = GetNode(1173); + var doc = GetContent(1173); var items = doc.Children(VariationContextAccessor).Take(4).ToIndexedArray(); @@ -315,7 +214,7 @@ public void Is_Last_From_Take() [Test] public void Is_Last_From_Skip() { - var doc = GetNode(1173); + var doc = GetContent(1173); foreach (var d in doc.Children(VariationContextAccessor).Skip(1).ToIndexedArray()) { @@ -333,10 +232,10 @@ public void Is_Last_From_Skip() [Test] public void Is_Last_From_Concat() { - var doc = GetNode(1173); + var doc = GetContent(1173); var items = doc.Children(VariationContextAccessor) - .Concat(new[] { GetNode(1175), GetNode(4444) }) + .Concat(new[] { GetContent(1175), GetContent(4444) }) .ToIndexedArray(); foreach (var item in items) @@ -355,7 +254,7 @@ public void Is_Last_From_Concat() [Test] public void Descendants_Ordered_Properly() { - var doc = GetNode(1046); + var doc = GetContent(1046); var expected = new[] { 1046, 1173, 1174, 117, 1177, 1178, 1179, 1176, 1175, 4444, 1172 }; var exindex = 0; @@ -370,9 +269,11 @@ public void Descendants_Ordered_Properly() [Test] public void Get_Property_Value_Recursive() { - var doc = GetNode(1174); - var rVal = doc.Value(Factory.GetRequiredService(), "testRecursive", fallback: Fallback.ToAncestors); - var nullVal = doc.Value(Factory.GetRequiredService(), "DoNotFindThis", fallback: Fallback.ToAncestors); + // TODO: We need to use a different fallback? + + var doc = GetContent(1174); + var rVal = doc.Value(PublishedValueFallback, "testRecursive", fallback: Fallback.ToAncestors); + var nullVal = doc.Value(PublishedValueFallback, "DoNotFindThis", fallback: Fallback.ToAncestors); Assert.AreEqual("This is the recursive val", rVal); Assert.AreEqual(null, nullVal); } @@ -380,17 +281,17 @@ public void Get_Property_Value_Recursive() [Test] public void Get_Property_Value_Uses_Converter() { - var doc = GetNode(1173); + var doc = GetContent(1173); - var propVal = doc.Value(Mock.Of(), "content"); + var propVal = doc.Value(PublishedValueFallback, "content"); Assert.IsInstanceOf(typeof(IHtmlEncodedString), propVal); Assert.AreEqual("
This is some content
", propVal.ToString()); - var propVal2 = doc.Value(Mock.Of(), "content"); + var propVal2 = doc.Value(PublishedValueFallback, "content"); Assert.IsInstanceOf(typeof(IHtmlEncodedString), propVal2); Assert.AreEqual("
This is some content
", propVal2.ToString()); - var propVal3 = doc.Value(Mock.Of(), "Content"); + var propVal3 = doc.Value(PublishedValueFallback, "Content"); Assert.IsInstanceOf(typeof(IHtmlEncodedString), propVal3); Assert.AreEqual("
This is some content
", propVal3.ToString()); } @@ -398,12 +299,12 @@ public void Get_Property_Value_Uses_Converter() [Test] public void Complex_Linq() { - var doc = GetNode(1173); + var doc = GetContent(1173); var result = doc.Ancestors().OrderBy(x => x.Level) .Single() .Descendants(Mock.Of()) - .FirstOrDefault(x => x.Value(Mock.Of(), "selectedNodes", defaultValue: "").Split(',').Contains("1173")); + .FirstOrDefault(x => x.Value(PublishedValueFallback, "selectedNodes", fallback: Fallback.ToDefaultValue, defaultValue: "").Split(',').Contains("1173")); Assert.IsNotNull(result); } @@ -411,16 +312,16 @@ public void Complex_Linq() [Test] public void Children_GroupBy_DocumentTypeAlias() { - var home = new AutoPublishedContentType(Guid.NewGuid(), 22, "Home", new PublishedPropertyType[] { }); - var custom = new AutoPublishedContentType(Guid.NewGuid(), 23, "CustomDocument", new PublishedPropertyType[] { }); - var contentTypes = new Dictionary - { - { home.Alias, home }, - { custom.Alias, custom } - }; - ContentTypesCache.GetPublishedContentTypeByAlias = alias => contentTypes[alias]; + //var home = new AutoPublishedContentType(Guid.NewGuid(), 22, "Home", new PublishedPropertyType[] { }); + //var custom = new AutoPublishedContentType(Guid.NewGuid(), 23, "CustomDocument", new PublishedPropertyType[] { }); + //var contentTypes = new Dictionary + //{ + // { home.Alias, home }, + // { custom.Alias, custom } + //}; + //ContentTypesCache.GetPublishedContentTypeByAlias = alias => contentTypes[alias]; - var doc = GetNode(1046); + var doc = GetContent(1046); var found1 = doc.Children(VariationContextAccessor).GroupBy(x => x.ContentType.Alias).ToArray(); @@ -432,16 +333,16 @@ public void Children_GroupBy_DocumentTypeAlias() [Test] public void Children_Where_DocumentTypeAlias() { - var home = new AutoPublishedContentType(Guid.NewGuid(), 22, "Home", new PublishedPropertyType[] { }); - var custom = new AutoPublishedContentType(Guid.NewGuid(), 23, "CustomDocument", new PublishedPropertyType[] { }); - var contentTypes = new Dictionary - { - { home.Alias, home }, - { custom.Alias, custom } - }; - ContentTypesCache.GetPublishedContentTypeByAlias = alias => contentTypes[alias]; + //var home = new AutoPublishedContentType(Guid.NewGuid(), 22, "Home", new PublishedPropertyType[] { }); + //var custom = new AutoPublishedContentType(Guid.NewGuid(), 23, "CustomDocument", new PublishedPropertyType[] { }); + //var contentTypes = new Dictionary + //{ + // { home.Alias, home }, + // { custom.Alias, custom } + //}; + //ContentTypesCache.GetPublishedContentTypeByAlias = alias => contentTypes[alias]; - var doc = GetNode(1046); + var doc = GetContent(1046); var found1 = doc.Children(VariationContextAccessor).Where(x => x.ContentType.Alias == "CustomDocument"); var found2 = doc.Children(VariationContextAccessor).Where(x => x.ContentType.Alias == "Home"); @@ -453,7 +354,7 @@ public void Children_Where_DocumentTypeAlias() [Test] public void Children_Order_By_Update_Date() { - var doc = GetNode(1173); + var doc = GetContent(1173); var ordered = doc.Children(VariationContextAccessor).OrderBy(x => x.UpdateDate); @@ -468,12 +369,12 @@ public void Children_Order_By_Update_Date() [Test] public void FirstChild() { - var doc = GetNode(1173); // has child nodes + var doc = GetContent(1173); // has child nodes Assert.IsNotNull(doc.FirstChild(Mock.Of())); Assert.IsNotNull(doc.FirstChild(Mock.Of(), x => true)); Assert.IsNotNull(doc.FirstChild(Mock.Of())); - doc = GetNode(1175); // does not have child nodes + doc = GetContent(1175); // does not have child nodes Assert.IsNull(doc.FirstChild(Mock.Of())); Assert.IsNull(doc.FirstChild(Mock.Of(), x => true)); Assert.IsNull(doc.FirstChild(Mock.Of())); @@ -482,7 +383,7 @@ public void FirstChild() [Test] public void FirstChildAsT() { - var doc = GetNode(1046); // has child nodes + var doc = GetContent(1046); // has child nodes var model = doc.FirstChild(Mock.Of(), x => true); // predicate @@ -491,7 +392,7 @@ public void FirstChildAsT() Assert.IsInstanceOf(model); Assert.IsInstanceOf(model); - doc = GetNode(1175); // does not have child nodes + doc = GetContent(1175); // does not have child nodes Assert.IsNull(doc.FirstChild(Mock.Of())); Assert.IsNull(doc.FirstChild(Mock.Of(), x => true)); } @@ -499,7 +400,7 @@ public void FirstChildAsT() [Test] public void IsComposedOf() { - var doc = GetNode(1173); + var doc = GetContent(1173); var isComposedOf = doc.IsComposedOf("MyCompositionAlias"); @@ -509,7 +410,7 @@ public void IsComposedOf() [Test] public void HasProperty() { - var doc = GetNode(1173); + var doc = GetContent(1173); var hasProp = doc.HasProperty(Constants.Conventions.Content.UrlAlias); @@ -519,7 +420,7 @@ public void HasProperty() [Test] public void HasValue() { - var doc = GetNode(1173); + var doc = GetContent(1173); var hasValue = doc.HasValue(Mock.Of(), Constants.Conventions.Content.UrlAlias); var noValue = doc.HasValue(Mock.Of(), "blahblahblah"); @@ -531,7 +432,7 @@ public void HasValue() [Test] public void Ancestors_Where_Visible() { - var doc = GetNode(1174); + var doc = GetContent(1174); var whereVisible = doc.Ancestors().Where(x => x.IsVisible(Mock.Of())); Assert.AreEqual(1, whereVisible.Count()); @@ -541,8 +442,8 @@ public void Ancestors_Where_Visible() [Test] public void Visible() { - var hidden = GetNode(1046); - var visible = GetNode(1173); + var hidden = GetContent(1046); + var visible = GetContent(1173); Assert.IsFalse(hidden.IsVisible(Mock.Of())); Assert.IsTrue(visible.IsVisible(Mock.Of())); @@ -551,7 +452,7 @@ public void Visible() [Test] public void Ancestor_Or_Self() { - var doc = GetNode(1173); + var doc = GetContent(1173); var result = doc.AncestorOrSelf(); @@ -564,7 +465,7 @@ public void Ancestor_Or_Self() [Test] public void U4_4559() { - var doc = GetNode(1174); + var doc = GetContent(1174); var result = doc.AncestorOrSelf(1); Assert.IsNotNull(result); Assert.AreEqual(1046, result.Id); @@ -573,27 +474,27 @@ public void U4_4559() [Test] public void Ancestors_Or_Self() { - var doc = GetNode(1174); + var doc = GetContent(1174); var result = doc.AncestorsOrSelf().ToArray(); Assert.IsNotNull(result); Assert.AreEqual(3, result.Length); - Assert.IsTrue(result.Select(x => ((dynamic)x).GetId()).ContainsAll(new dynamic[] { 1174, 1173, 1046 })); + Assert.IsTrue(result.Select(x => x.Id).ContainsAll(new [] { 1174, 1173, 1046 })); } [Test] public void Ancestors() { - var doc = GetNode(1174); + var doc = GetContent(1174); var result = doc.Ancestors().ToArray(); Assert.IsNotNull(result); Assert.AreEqual(2, result.Length); - Assert.IsTrue(result.Select(x => ((dynamic)x).GetId()).ContainsAll(new dynamic[] { 1173, 1046 })); + Assert.IsTrue(result.Select(x => x.Id).ContainsAll(new [] { 1173, 1046 })); } [Test] @@ -607,12 +508,12 @@ public void IsAncestor() // -- Custom Doc4: 117 (parent 1173) // - Custom Doc3: 1172 (no parent) - var home = GetNode(1173); - var root = GetNode(1046); - var customDoc = GetNode(1178); - var customDoc2 = GetNode(1179); - var customDoc3 = GetNode(1172); - var customDoc4 = GetNode(117); + var home = GetContent(1173); + var root = GetContent(1046); + var customDoc = GetContent(1178); + var customDoc2 = GetContent(1179); + var customDoc3 = GetContent(1172); + var customDoc4 = GetContent(117); Assert.IsTrue(root.IsAncestor(customDoc4)); Assert.IsFalse(root.IsAncestor(customDoc3)); @@ -656,12 +557,12 @@ public void IsAncestorOrSelf() // -- Custom Doc4: 117 (parent 1173) // - Custom Doc3: 1172 (no parent) - var home = GetNode(1173); - var root = GetNode(1046); - var customDoc = GetNode(1178); - var customDoc2 = GetNode(1179); - var customDoc3 = GetNode(1172); - var customDoc4 = GetNode(117); + var home = GetContent(1173); + var root = GetContent(1046); + var customDoc = GetContent(1178); + var customDoc2 = GetContent(1179); + var customDoc3 = GetContent(1172); + var customDoc4 = GetContent(117); Assert.IsTrue(root.IsAncestorOrSelf(customDoc4)); Assert.IsFalse(root.IsAncestorOrSelf(customDoc3)); @@ -699,27 +600,27 @@ public void IsAncestorOrSelf() [Test] public void Descendants_Or_Self() { - var doc = GetNode(1046); + var doc = GetContent(1046); var result = doc.DescendantsOrSelf(Mock.Of()).ToArray(); Assert.IsNotNull(result); Assert.AreEqual(10, result.Count()); - Assert.IsTrue(result.Select(x => ((dynamic)x).GetId()).ContainsAll(new dynamic[] { 1046, 1173, 1174, 1176, 1175 })); + Assert.IsTrue(result.Select(x => x.Id).ContainsAll(new [] { 1046, 1173, 1174, 1176, 1175 })); } [Test] public void Descendants() { - var doc = GetNode(1046); + var doc = GetContent(1046); var result = doc.Descendants(Mock.Of()).ToArray(); Assert.IsNotNull(result); Assert.AreEqual(9, result.Count()); - Assert.IsTrue(result.Select(x => ((dynamic)x).GetId()).ContainsAll(new dynamic[] { 1173, 1174, 1176, 1175, 4444 })); + Assert.IsTrue(result.Select(x => x.Id).ContainsAll(new [] { 1173, 1174, 1176, 1175, 4444 })); } [Test] @@ -733,12 +634,12 @@ public void IsDescendant() // -- Custom Doc4: 117 (parent 1173) // - Custom Doc3: 1172 (no parent) - var home = GetNode(1173); - var root = GetNode(1046); - var customDoc = GetNode(1178); - var customDoc2 = GetNode(1179); - var customDoc3 = GetNode(1172); - var customDoc4 = GetNode(117); + var home = GetContent(1173); + var root = GetContent(1046); + var customDoc = GetContent(1178); + var customDoc2 = GetContent(1179); + var customDoc3 = GetContent(1172); + var customDoc4 = GetContent(117); Assert.IsFalse(root.IsDescendant(root)); Assert.IsFalse(root.IsDescendant(home)); @@ -782,12 +683,12 @@ public void IsDescendantOrSelf() // -- Custom Doc4: 117 (parent 1173) // - Custom Doc3: 1172 (no parent) - var home = GetNode(1173); - var root = GetNode(1046); - var customDoc = GetNode(1178); - var customDoc2 = GetNode(1179); - var customDoc3 = GetNode(1172); - var customDoc4 = GetNode(117); + var home = GetContent(1173); + var root = GetContent(1046); + var customDoc = GetContent(1178); + var customDoc2 = GetContent(1179); + var customDoc3 = GetContent(1172); + var customDoc4 = GetContent(117); Assert.IsTrue(root.IsDescendantOrSelf(root)); Assert.IsFalse(root.IsDescendantOrSelf(home)); @@ -830,39 +731,40 @@ public void SiblingsAndSelf() // --- Level1.1.2: 117 (parent 1173) // --- Level1.1.3: 1177 (parent 1173) // --- Level1.1.4: 1178 (parent 1173) + // ---- Level1.1.4.1: 1179 (parent 1178) // --- Level1.1.5: 1176 (parent 1173) // -- Level1.2: 1175 (parent 1046) // -- Level1.3: 4444 (parent 1046) - var root = GetNode(1046); - var level1_1 = GetNode(1173); - var level1_1_1 = GetNode(1174); - var level1_1_2 = GetNode(117); - var level1_1_3 = GetNode(1177); - var level1_1_4 = GetNode(1178); - var level1_1_5 = GetNode(1176); - var level1_2 = GetNode(1175); - var level1_3 = GetNode(4444); + // - Root : 1172 (no parent) - _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot(It.IsAny())).Returns(new []{root}); + var root = GetContent(1046); + var level1_1 = GetContent(1173); + var level1_1_1 = GetContent(1174); + var level1_1_2 = GetContent(117); + var level1_1_3 = GetContent(1177); + var level1_1_4 = GetContent(1178); + var level1_1_5 = GetContent(1176); + var level1_2 = GetContent(1175); + var level1_3 = GetContent(4444); + var root2 = GetContent(1172); - var variationContextAccessor = Factory.GetRequiredService(); - var publishedSnapshot = _publishedSnapshotAccessorMock.Object.PublishedSnapshot; + var publishedSnapshot = GetPublishedSnapshot(); - CollectionAssertAreEqual(new []{root}, root.SiblingsAndSelf(publishedSnapshot, variationContextAccessor)); + CollectionAssertAreEqual(new[] { root, root2 }, root.SiblingsAndSelf(publishedSnapshot, VariationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_1.SiblingsAndSelf(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_2.SiblingsAndSelf(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_3.SiblingsAndSelf(publishedSnapshot, variationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1, level1_2, level1_3 }, level1_1.SiblingsAndSelf(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1, level1_2, level1_3 }, level1_2.SiblingsAndSelf(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1, level1_2, level1_3 }, level1_3.SiblingsAndSelf(publishedSnapshot, VariationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_1.SiblingsAndSelf(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_2.SiblingsAndSelf(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_3.SiblingsAndSelf(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_4.SiblingsAndSelf(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_5.SiblingsAndSelf(publishedSnapshot, variationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5 }, level1_1_1.SiblingsAndSelf(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5 }, level1_1_2.SiblingsAndSelf(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5 }, level1_1_3.SiblingsAndSelf(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5 }, level1_1_4.SiblingsAndSelf(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5 }, level1_1_5.SiblingsAndSelf(publishedSnapshot, VariationContextAccessor)); } - [Test] + [Test] public void Siblings() { // Structure: @@ -872,40 +774,41 @@ public void Siblings() // --- Level1.1.2: 117 (parent 1173) // --- Level1.1.3: 1177 (parent 1173) // --- Level1.1.4: 1178 (parent 1173) + // ---- Level1.1.4.1: 1179 (parent 1178) // --- Level1.1.5: 1176 (parent 1173) // -- Level1.2: 1175 (parent 1046) // -- Level1.3: 4444 (parent 1046) - var root = GetNode(1046); - var level1_1 = GetNode(1173); - var level1_1_1 = GetNode(1174); - var level1_1_2 = GetNode(117); - var level1_1_3 = GetNode(1177); - var level1_1_4 = GetNode(1178); - var level1_1_5 = GetNode(1176); - var level1_2 = GetNode(1175); - var level1_3 = GetNode(4444); + // - Root : 1172 (no parent) - _publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot(It.IsAny())).Returns(new []{root}); + var root = GetContent(1046); + var level1_1 = GetContent(1173); + var level1_1_1 = GetContent(1174); + var level1_1_2 = GetContent(117); + var level1_1_3 = GetContent(1177); + var level1_1_4 = GetContent(1178); + var level1_1_5 = GetContent(1176); + var level1_2 = GetContent(1175); + var level1_3 = GetContent(4444); + var root2 = GetContent(1172); - var variationContextAccessor = Factory.GetRequiredService(); - var publishedSnapshot = _publishedSnapshotAccessorMock.Object.PublishedSnapshot; + var publishedSnapshot = GetPublishedSnapshot(); - CollectionAssertAreEqual(new IPublishedContent[0], root.Siblings(publishedSnapshot, variationContextAccessor)); + CollectionAssertAreEqual(new[] { root2 }, root.Siblings(publishedSnapshot, VariationContextAccessor)); - CollectionAssertAreEqual( new []{level1_2, level1_3}, level1_1.Siblings(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1, level1_3}, level1_2.Siblings(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1, level1_2}, level1_3.Siblings(publishedSnapshot, variationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_2, level1_3 }, level1_1.Siblings(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1, level1_3 }, level1_2.Siblings(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1, level1_2 }, level1_3.Siblings(publishedSnapshot, VariationContextAccessor)); - CollectionAssertAreEqual( new []{ level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_1.Siblings(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1_1, level1_1_3, level1_1_4, level1_1_5}, level1_1_2.Siblings(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_4, level1_1_5}, level1_1_3.Siblings(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_5}, level1_1_4.Siblings(publishedSnapshot, variationContextAccessor)); - CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4}, level1_1_5.Siblings(publishedSnapshot, variationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1_2, level1_1_3, level1_1_4, level1_1_5 }, level1_1_1.Siblings(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1_1, level1_1_3, level1_1_4, level1_1_5 }, level1_1_2.Siblings(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1_1, level1_1_2, level1_1_4, level1_1_5 }, level1_1_3.Siblings(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1_1, level1_1_2, level1_1_3, level1_1_5 }, level1_1_4.Siblings(publishedSnapshot, VariationContextAccessor)); + CollectionAssertAreEqual(new[] { level1_1_1, level1_1_2, level1_1_3, level1_1_4 }, level1_1_5.Siblings(publishedSnapshot, VariationContextAccessor)); } private void CollectionAssertAreEqual(IEnumerable expected, IEnumerable actual) - where T: IPublishedContent + where T : IPublishedContent { var e = expected.Select(x => x.Id); var a = actual.Select(x => x.Id); @@ -915,37 +818,26 @@ private void CollectionAssertAreEqual(IEnumerable expected, IEnumerable [Test] public void FragmentProperty() { - var factory = Factory.GetRequiredService() as PublishedContentTypeFactory; - IEnumerable CreatePropertyTypes(IPublishedContentType contentType) { - yield return factory.CreatePropertyType(contentType, "detached", 1003); + yield return PublishedContentTypeFactory.CreatePropertyType(contentType, "detached", _dataTypes[0].Id); } - var ct = factory.CreateContentType(Guid.NewGuid(), 0, "alias", CreatePropertyTypes); + var ct = PublishedContentTypeFactory.CreateContentType(Guid.NewGuid(), 0, "alias", CreatePropertyTypes); var pt = ct.GetPropertyType("detached"); var prop = new PublishedElementPropertyBase(pt, null, false, PropertyCacheLevel.None, 5548); Assert.IsInstanceOf(prop.GetValue()); Assert.AreEqual(5548, prop.GetValue()); } - public void Fragment1() - { - var type = ContentTypesCache.Get(PublishedItemType.Content, "detachedSomething"); - var values = new Dictionary(); - var f = new PublishedElement(type, Guid.NewGuid(), values, false); - } - [Test] public void Fragment2() { - var factory = Factory.GetRequiredService() as PublishedContentTypeFactory; - IEnumerable CreatePropertyTypes(IPublishedContentType contentType) { - yield return factory.CreatePropertyType(contentType, "legend", 1004); - yield return factory.CreatePropertyType(contentType, "image", 1005); - yield return factory.CreatePropertyType(contentType, "size", 1003); + yield return PublishedContentTypeFactory.CreatePropertyType(contentType, "legend", _dataTypes[0].Id); + yield return PublishedContentTypeFactory.CreatePropertyType(contentType, "image", _dataTypes[0].Id); + yield return PublishedContentTypeFactory.CreatePropertyType(contentType, "size", _dataTypes[0].Id); } const string val1 = "boom bam"; @@ -954,7 +846,7 @@ IEnumerable CreatePropertyTypes(IPublishedContentType co var guid = Guid.NewGuid(); - var ct = factory.CreateContentType(Guid.NewGuid(), 0, "alias", CreatePropertyTypes); + var ct = PublishedContentTypeFactory.CreateContentType(Guid.NewGuid(), 0, "alias", CreatePropertyTypes); var c = new ImageWithLegendModel(ct, guid, new Dictionary { @@ -967,6 +859,87 @@ IEnumerable CreatePropertyTypes(IPublishedContentType co Assert.AreEqual(val3, c.Size); } + [Test] + public void First() + { + var publishedSnapshot = GetPublishedSnapshot(); + var content = publishedSnapshot.Content.GetAtRoot().First(); + Assert.AreEqual("Home", content.Name(VariationContextAccessor)); + } + + [Test] + public void Distinct() + { + var items = GetContent(1173) + .Children(VariationContextAccessor) + .Distinct() + .Distinct() + .ToIndexedArray(); + + Assert.AreEqual(5, items.Length); + + IndexedArrayItem item = items[0]; + Assert.AreEqual(1174, item.Content.Id); + Assert.IsTrue(item.IsFirst()); + Assert.IsFalse(item.IsLast()); + + item = items[^1]; + Assert.AreEqual(1176, item.Content.Id); + Assert.IsFalse(item.IsFirst()); + Assert.IsTrue(item.IsLast()); + } + + [Test] + public void OfType1() + { + var publishedSnapshot = GetPublishedSnapshot(); + var items = publishedSnapshot.Content.GetAtRoot() + .OfType() + .Distinct() + .ToIndexedArray(); + Assert.AreEqual(1, items.Length); + Assert.IsInstanceOf(items.First().Content); + } + + [Test] + public void OfType2() + { + var publishedSnapshot = GetPublishedSnapshot(); + var content = publishedSnapshot.Content.GetAtRoot() + .OfType() + .Distinct() + .ToIndexedArray(); + Assert.AreEqual(1, content.Length); + Assert.IsInstanceOf(content.First().Content); + } + + [Test] + public void OfType() + { + var content = GetContent(1173) + .Children(VariationContextAccessor) + .OfType() + .First(x => x.UmbracoNaviHide == true); + Assert.AreEqual(1176, content.Id); + } + + [Test] + public void Position() + { + var items = GetContent(1173).Children(VariationContextAccessor) + .Where(x => x.Value(Mock.Of(), "umbracoNaviHide") == 0) + .ToIndexedArray(); + + Assert.AreEqual(3, items.Length); + + Assert.IsTrue(items.First().IsFirst()); + Assert.IsFalse(items.First().IsLast()); + Assert.IsFalse(items.Skip(1).First().IsFirst()); + Assert.IsFalse(items.Skip(1).First().IsLast()); + Assert.IsFalse(items.Skip(2).First().IsFirst()); + Assert.IsTrue(items.Skip(2).First().IsLast()); + } + class ImageWithLegendModel : PublishedElement { public ImageWithLegendModel(IPublishedContentType contentType, Guid fragmentKey, Dictionary values, bool previewing) @@ -980,5 +953,31 @@ public ImageWithLegendModel(IPublishedContentType contentType, Guid fragmentKey, public int Size => this.Value(Mock.Of(), "size"); } + + //[PublishedModel("ContentType2")] + //public class ContentType2 : PublishedContentModel + //{ + // #region Plumbing + + // public ContentType2(IPublishedContent content, IPublishedValueFallback fallback) + // : base(content, fallback) + // { } + + // #endregion + + // public int Prop1 => this.Value(Mock.Of(), "prop1"); + //} + + //[PublishedModel("ContentType2Sub")] + //public class ContentType2Sub : ContentType2 + //{ + // #region Plumbing + + // public ContentType2Sub(IPublishedContent content, IPublishedValueFallback fallback) + // : base(content, fallback) + // { } + + // #endregion + //} } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedMediaTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedMediaTests.cs new file mode 100644 index 000000000000..e24383855c8d --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedMediaTests.cs @@ -0,0 +1,242 @@ +using System.Collections.Generic; +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.PropertyEditors; +using Umbraco.Cms.Core.Strings; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; +using Umbraco.Cms.Infrastructure.Serialization; +using Umbraco.Cms.Tests.Common.Builders; +using Umbraco.Cms.Tests.Common.Builders.Extensions; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache +{ + /// + /// Tests the typed extension methods on IPublishedContent using the DefaultPublishedMediaStore + /// + [TestFixture] + public class PublishedMediaTests : PublishedSnapshotServiceTestBase + { + [SetUp] + public override void Setup() + { + base.Setup(); + + var dataTypes = GetDefaultDataTypes().ToList(); + var serializer = new ConfigurationEditorJsonSerializer(); + var rteDataType = new DataType(new VoidEditor("RTE", Mock.Of()), serializer) { Id = 4 }; + dataTypes.Add(rteDataType); + _dataTypes = dataTypes.ToArray(); + + _propertyDataTypes = new() + { + // defaults will just use the first one + [string.Empty] = _dataTypes[0], + + // content uses the RTE + ["content"] = _dataTypes[1] + }; + } + + private Dictionary _propertyDataTypes; + private DataType[] _dataTypes; + + private ContentNodeKit CreateRoot(out MediaType mediaType) + { + mediaType = new MediaType(ShortStringHelper, -1); + + ContentData item1Data = new ContentDataBuilder() + .WithName("Content 1") + .WithProperties(new PropertyDataBuilder() + .WithPropertyData("content", "
This is some content
") + .Build()) + // build with a dynamically created media type + .Build(ShortStringHelper, _propertyDataTypes, mediaType, "image2"); + + ContentNodeKit item1 = ContentNodeKitBuilder.CreateWithContent( + mediaType.Id, + 1, "-1,1", + draftData: item1Data, + publishedData: item1Data); + + return item1; + } + + private IEnumerable CreateChildren( + int startId, + ContentNodeKit parent, + IMediaType mediaType, + int count) + { + for (int i = 0; i < count; i++) + { + var id = startId + i + 1; + + ContentData item1Data = new ContentDataBuilder() + .WithName("Child " + id) + .WithProperties(new PropertyDataBuilder() + .WithPropertyData("content", "
This is some content
") + .Build()) + .Build(); + + var parentPath = parent.Node.Path; + + ContentNodeKit item1 = ContentNodeKitBuilder.CreateWithContent( + mediaType.Id, + id, $"{parentPath},{id}", + draftData: item1Data, + publishedData: item1Data); + + yield return item1; + } + } + + private void InitializeWithHierarchy( + out int rootId, + out IReadOnlyList firstLevelChildren, + out IReadOnlyList secondLevelChildren) + { + var cache = new List(); + var root = CreateRoot(out MediaType mediaType); + firstLevelChildren = CreateChildren(10, root, mediaType, 3).ToList(); + secondLevelChildren = CreateChildren(20, firstLevelChildren[0], mediaType, 3).ToList(); + cache.Add(root); + cache.AddRange(firstLevelChildren); + cache.AddRange(secondLevelChildren); + InitializedCache(null, null, _dataTypes, cache, new[] { mediaType }); + rootId = root.Node.Id; + } + + [Test] + public void Get_Property_Value_Uses_Converter() + { + var cache = CreateRoot(out MediaType mediaType); + InitializedCache(null, null, _dataTypes.ToArray(), new[] { cache }, new[] { mediaType }); + + var publishedMedia = GetMedia(1); + + var propVal = publishedMedia.Value(PublishedValueFallback, "content"); + Assert.IsInstanceOf(propVal); + Assert.AreEqual("
This is some content
", propVal.ToString()); + + var propVal2 = publishedMedia.Value(PublishedValueFallback, "content"); + Assert.IsInstanceOf(propVal2); + Assert.AreEqual("
This is some content
", propVal2.ToString()); + + var propVal3 = publishedMedia.Value(PublishedValueFallback, "Content"); + Assert.IsInstanceOf(propVal3); + Assert.AreEqual("
This is some content
", propVal3.ToString()); + } + + [Test] + public void Children() + { + InitializeWithHierarchy( + out var rootId, + out IReadOnlyList firstLevelChildren, + out IReadOnlyList secondLevelChildren); + + var publishedMedia = GetMedia(rootId); + + var rootChildren = publishedMedia.Children(VariationContextAccessor); + Assert.IsTrue(rootChildren.Select(x => x.Id).ContainsAll(firstLevelChildren.Select(x => x.Node.Id))); + + var publishedChild1 = GetMedia(firstLevelChildren[0].Node.Id); + var subChildren = publishedChild1.Children(VariationContextAccessor); + Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(secondLevelChildren.Select(x => x.Node.Id))); + } + + [Test] + public void Descendants() + { + InitializeWithHierarchy( + out var rootId, + out IReadOnlyList firstLevelChildren, + out IReadOnlyList secondLevelChildren); + + var publishedMedia = GetMedia(rootId); + var rootDescendants = publishedMedia.Descendants(VariationContextAccessor); + + var descendentIds = firstLevelChildren.Select(x => x.Node.Id).Concat(secondLevelChildren.Select(x => x.Node.Id)); + + Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(descendentIds)); + + var publishedChild1 = GetMedia(firstLevelChildren[0].Node.Id); + var subDescendants = publishedChild1.Descendants(VariationContextAccessor); + Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(secondLevelChildren.Select(x => x.Node.Id))); + } + + [Test] + public void DescendantsOrSelf() + { + InitializeWithHierarchy( + out var rootId, + out IReadOnlyList firstLevelChildren, + out IReadOnlyList secondLevelChildren); + + var publishedMedia = GetMedia(rootId); + var rootDescendantsOrSelf = publishedMedia.DescendantsOrSelf(VariationContextAccessor); + var descendentAndSelfIds = firstLevelChildren.Select(x => x.Node.Id) + .Concat(secondLevelChildren.Select(x => x.Node.Id)) + .Append(rootId); + + Assert.IsTrue(rootDescendantsOrSelf.Select(x => x.Id).ContainsAll(descendentAndSelfIds)); + + var publishedChild1 = GetMedia(firstLevelChildren[0].Node.Id); + var subDescendantsOrSelf = publishedChild1.DescendantsOrSelf(VariationContextAccessor); + Assert.IsTrue(subDescendantsOrSelf.Select(x => x.Id).ContainsAll( + secondLevelChildren.Select(x => x.Node.Id).Append(firstLevelChildren[0].Node.Id))); + } + + [Test] + public void Parent() + { + InitializeWithHierarchy( + out var rootId, + out IReadOnlyList firstLevelChildren, + out IReadOnlyList secondLevelChildren); + + var publishedMedia = GetMedia(rootId); + Assert.AreEqual(null, publishedMedia.Parent); + + var publishedChild1 = GetMedia(firstLevelChildren[0].Node.Id); + Assert.AreEqual(publishedMedia.Id, publishedChild1.Parent.Id); + + var publishedSubChild1 = GetMedia(secondLevelChildren[0].Node.Id); + Assert.AreEqual(firstLevelChildren[0].Node.Id, publishedSubChild1.Parent.Id); + } + + [Test] + public void Ancestors() + { + InitializeWithHierarchy( + out var rootId, + out IReadOnlyList firstLevelChildren, + out IReadOnlyList secondLevelChildren); + + var publishedSubChild1 = GetMedia(secondLevelChildren[0].Node.Id); + Assert.IsTrue(publishedSubChild1.Ancestors().Select(x => x.Id) + .ContainsAll(new[] { firstLevelChildren[0].Node.Id, rootId })); + } + + [Test] + public void AncestorsOrSelf() + { + InitializeWithHierarchy( + out var rootId, + out IReadOnlyList firstLevelChildren, + out IReadOnlyList secondLevelChildren); + + var publishedSubChild1 = GetMedia(secondLevelChildren[0].Node.Id); + Assert.IsTrue(publishedSubChild1.AncestorsOrSelf().Select(x => x.Id) + .ContainsAll(new[] { secondLevelChildren[0].Node.Id, firstLevelChildren[0].Node.Id, rootId })); + } + + + } +} diff --git a/tests/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceCollectionTests.cs similarity index 59% rename from tests/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceCollectionTests.cs index 17e78647f24d..532c7ccf4b0c 100644 --- a/tests/Umbraco.Tests/PublishedContent/NuCacheChildrenTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceCollectionTests.cs @@ -1,77 +1,30 @@ using System; using System.Collections.Generic; -using System.Data; using System.Linq; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Moq; using NUnit.Framework; -using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Services.Changes; -using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.PublishedCache; using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; -using Umbraco.Cms.Infrastructure.Serialization; -using Umbraco.Cms.Tests.Common; +using Umbraco.Cms.Tests.Common.Builders; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing.Objects; -using Umbraco.Web.Composing; -namespace Umbraco.Tests.PublishedContent +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache { [TestFixture] - public class NuCacheChildrenTests + public class PublishedSnapshotServiceCollectionTests : PublishedSnapshotServiceTestBase { - private IPublishedModelFactory PublishedModelFactory { get; } = new NoopPublishedModelFactory(); - private IVariationContextAccessor VariationContextAccessor { get; } = TestHelper.VariationContextAccessor; - - private IPublishedSnapshotService _snapshotService; - private IVariationContextAccessor _variationAccesor; - private IPublishedSnapshotAccessor _snapshotAccessor; private ContentType _contentTypeInvariant; private ContentType _contentTypeVariant; - private TestDataSource _source; - private IContentCacheDataSerializerFactory _contentNestedDataSerializerFactory; - - [TearDown] - public void Teardown() - { - _snapshotService?.Dispose(); - } + private ContentType[] _contentTypes; - private void Init(Func> kits) + [SetUp] + public override void Setup() { - var factory = Mock.Of(); - Current.Factory = factory; - - var hostingEnvironment = Mock.Of(); - - Mock.Get(factory).Setup(x => x.GetService(typeof(IPublishedModelFactory))).Returns(PublishedModelFactory); - - var runtime = Mock.Of(); - Mock.Get(runtime).Setup(x => x.Level).Returns(RuntimeLevel.Run); - - var serializer = new ConfigurationEditorJsonSerializer(); - - // create data types, property types and content types - var dataType = new DataType(new VoidEditor("Editor", Mock.Of()), serializer) { Id = 3 }; - - var dataTypes = new[] - { - dataType - }; + base.Setup(); var propertyType = new PropertyType(TestHelper.ShortStringHelper, "Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.Nothing }; _contentTypeInvariant = new ContentType(TestHelper.ShortStringHelper, -1) { Id = 2, Alias = "itype", Variations = ContentVariation.Nothing }; @@ -81,94 +34,11 @@ private void Init(Func> kits) _contentTypeVariant = new ContentType(TestHelper.ShortStringHelper, -1) { Id = 3, Alias = "vtype", Variations = ContentVariation.Culture }; _contentTypeVariant.AddPropertyType(propertyType); - var contentTypes = new[] + _contentTypes = new[] { _contentTypeInvariant, _contentTypeVariant }; - - var contentTypeService = new Mock(); - contentTypeService.Setup(x => x.GetAll()).Returns(contentTypes); - contentTypeService.Setup(x => x.GetAll(It.IsAny())).Returns(contentTypes); - - var mediaTypeService = new Mock(); - mediaTypeService.Setup(x => x.GetAll()).Returns(Enumerable.Empty()); - mediaTypeService.Setup(x => x.GetAll(It.IsAny())).Returns(Enumerable.Empty()); - - var contentTypeServiceBaseFactory = new Mock(); - contentTypeServiceBaseFactory.Setup(x => x.For(It.IsAny())).Returns(contentTypeService.Object); - - var dataTypeService = Mock.Of(); - Mock.Get(dataTypeService).Setup(x => x.GetAll()).Returns(dataTypes); - - // create a service context - var serviceContext = ServiceContext.CreatePartial( - dataTypeService: dataTypeService, - memberTypeService: Mock.Of(), - memberService: Mock.Of(), - contentTypeService: contentTypeService.Object, - mediaTypeService: mediaTypeService.Object, - localizationService: Mock.Of(), - domainService: Mock.Of() - ); - - // create a scope provider - var scopeProvider = new Mock(); - scopeProvider - .Setup(x => x.CreateScope( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns(Mock.Of); - - // create a published content type factory - var contentTypeFactory = new PublishedContentTypeFactory( - PublishedModelFactory, - new PropertyValueConverterCollection(Array.Empty()), - dataTypeService); - - // create accessors - _variationAccesor = new TestVariationContextAccessor(); - _snapshotAccessor = new TestPublishedSnapshotAccessor(); - - // create a data source for NuCache - _source = new TestDataSource(kits()); - _contentNestedDataSerializerFactory = new JsonContentNestedDataSerializerFactory(); - - var typeFinder = TestHelper.GetTypeFinder(); - - var globalSettings = new GlobalSettings(); - var nuCacheSettings = new NuCacheSettings(); - - // at last, create the complete NuCache snapshot service! - var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true }; - _snapshotService = new PublishedSnapshotService( - options, - null, - serviceContext, - contentTypeFactory, - _snapshotAccessor, - _variationAccesor, - Mock.Of(), - NullLoggerFactory.Instance, - scopeProvider.Object, - _source, - new TestDefaultCultureAccessor(), - Options.Create(globalSettings), - Mock.Of(), - PublishedModelFactory, - hostingEnvironment, - Options.Create(nuCacheSettings), - _contentNestedDataSerializerFactory); - - - // invariant is the current default - _variationAccesor.VariationContext = new VariationContext(); - - Mock.Get(factory).Setup(x => x.GetService(typeof(IVariationContextAccessor))).Returns(_variationAccesor); } private IEnumerable GetNestedVariantKits() @@ -224,25 +94,13 @@ private ContentNodeKit CreateInvariantKit(int id, int parentId, int sortOrder, D var level = path.Count(x => x == ','); var now = DateTime.Now; - var contentData = new ContentData - { - Name = "N" + id, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - }; + var contentData = ContentDataBuilder.CreateBasic("N" + id, now); - return new ContentNodeKit - { - ContentTypeId = _contentTypeInvariant.Id, - Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0), - DraftData = null, - PublishedData = contentData - }; + return ContentNodeKitBuilder.CreateWithContent( + _contentTypeInvariant.Id, + id, path, sortOrder, level, parentId, 0, Guid.NewGuid(), DateTime.Now, + null, + contentData); } private IEnumerable GetVariantKits() @@ -289,23 +147,16 @@ private ContentNodeKit CreateVariantKit(int id, int parentId, int sortOrder, Dic var level = path.Count(x => x == ','); var now = DateTime.Now; - return new ContentNodeKit - { - ContentTypeId = _contentTypeVariant.Id, - Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0), - DraftData = null, - PublishedData = new ContentData - { - Name = "N" + id, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = GetCultureInfos(id, now) - } - }; + var contentData = ContentDataBuilder.CreateVariant( + "N" + id, + GetCultureInfos(id, now), + now); + + return ContentNodeKitBuilder.CreateWithContent( + contentTypeId: _contentTypeVariant.Id, + id: id, path: path, sortOrder: sortOrder, level: level, parentContentId: parentId, + draftData: null, + publishedData: contentData); } private IEnumerable GetVariantWithDraftKits() @@ -334,28 +185,20 @@ ContentNodeKit CreateKit(int id, int parentId, int sortOrder) var level = path.Count(x => x == ','); var now = DateTime.Now; - ContentData CreateContentData(bool published) => new ContentData - { - Name = "N" + id, - Published = published, - TemplateId = 0, - VersionId = 1, - VersionDate = now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = GetCultureInfos(id, now) - }; + ContentData CreateContentData(bool published) => ContentDataBuilder.CreateVariant( + "N" + id, + GetCultureInfos(id, now), + now, + published); var withDraft = id % 2 == 0; var withPublished = !withDraft; - return new ContentNodeKit - { - ContentTypeId = _contentTypeVariant.Id, - Node = new ContentNode(id, Guid.NewGuid(), level, path, sortOrder, parentId, DateTime.Now, 0), - DraftData = withDraft ? CreateContentData(false) : null, - PublishedData = withPublished ? CreateContentData(true) : null - }; + return ContentNodeKitBuilder.CreateWithContent( + contentTypeId: _contentTypeVariant.Id, + id: id, path: path, sortOrder: sortOrder, level: level, parentContentId: parentId, + draftData: withDraft ? CreateContentData(false) : null, + publishedData: withPublished ? CreateContentData(true) : null); } yield return CreateKit(1, -1, 1); @@ -379,10 +222,9 @@ ContentNodeKit CreateKit(int id, int parentId, int sortOrder) [Test] public void EmptyTest() { - Init(() => Enumerable.Empty()); + InitializedCache(Array.Empty(), _contentTypes); - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + var snapshot = GetPublishedSnapshot(); var documents = snapshot.Content.GetAtRoot().ToArray(); Assert.AreEqual(0, documents.Length); @@ -391,37 +233,35 @@ public void EmptyTest() [Test] public void ChildrenTest() { - Init(GetInvariantKits); + InitializedCache(GetInvariantKits(), _contentTypes); - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + var snapshot = GetPublishedSnapshot(); var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2", "N3"); - documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N4", "N5", "N6"); - documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(2).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N9", "N8", "N7"); - documents = snapshot.Content.GetById(3).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(3).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N10"); - documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(4).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N11", "N12"); - documents = snapshot.Content.GetById(10).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(10).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents); } [Test] public void ParentTest() { - Init(GetInvariantKits); + InitializedCache(GetInvariantKits(), _contentTypes); - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + var snapshot = GetPublishedSnapshot(); Assert.IsNull(snapshot.Content.GetById(1).Parent); Assert.IsNull(snapshot.Content.GetById(2).Parent); @@ -444,40 +284,27 @@ public void ParentTest() [Test] public void MoveToRootTest() { - Init(GetInvariantKits); + InitializedCache(GetInvariantKits(), _contentTypes); // get snapshot - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + var snapshot = GetPublishedSnapshot(); // do some changes - var kit = _source.Kits[10]; - _source.Kits[10] = new ContentNodeKit - { - ContentTypeId = 2, - Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), 1, "-1,10", 4, -1, DateTime.Now, 0), - DraftData = null, - PublishedData = new ContentData - { - Name = kit.PublishedData.Name, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = DateTime.Now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - } - }; + var kit = NuCacheContentService.ContentKits[10]; + NuCacheContentService.ContentKits[10] = ContentNodeKitBuilder.CreateWithContent( + contentTypeId: _contentTypeInvariant.Id, + id: kit.Node.Id, path: "-1,10", sortOrder: 4, level: 1, parentContentId: -1, + draftData: null, + publishedData: ContentDataBuilder.CreateBasic(kit.PublishedData.Name)); // notify - _snapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(10, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _); + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(10, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _); // changes that *I* make are immediately visible on the current snapshot var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2", "N3", "N10"); - documents = snapshot.Content.GetById(3).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(3).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents); Assert.IsNull(snapshot.Content.GetById(10).Parent); @@ -486,40 +313,27 @@ public void MoveToRootTest() [Test] public void MoveFromRootTest() { - Init(GetInvariantKits); + InitializedCache(GetInvariantKits(), _contentTypes); // get snapshot - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + var snapshot = GetPublishedSnapshot(); // do some changes - var kit = _source.Kits[1]; - _source.Kits[1] = new ContentNodeKit - { - ContentTypeId = 2, - Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), 1, "-1,3,10,1", 1, 10, DateTime.Now, 0), - DraftData = null, - PublishedData = new ContentData - { - Name = kit.PublishedData.Name, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = DateTime.Now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - } - }; + var kit = NuCacheContentService.ContentKits[1]; + NuCacheContentService.ContentKits[1] = ContentNodeKitBuilder.CreateWithContent( + contentTypeId: _contentTypeInvariant.Id, + id: kit.Node.Id, path: "-1,3,10,1", sortOrder: 1, level: 1, parentContentId: 10, + draftData: null, + publishedData: ContentDataBuilder.CreateBasic(kit.PublishedData.Name)); // notify - _snapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _); + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _); // changes that *I* make are immediately visible on the current snapshot var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N2", "N3"); - documents = snapshot.Content.GetById(10).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(10).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N1"); Assert.AreEqual(10, snapshot.Content.GetById(1).Parent?.Id); @@ -528,166 +342,81 @@ public void MoveFromRootTest() [Test] public void ReOrderTest() { - Init(GetInvariantKits); + InitializedCache(GetInvariantKits(), _contentTypes); // get snapshot - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + var snapshot = GetPublishedSnapshot(); // do some changes - var kit = _source.Kits[7]; - _source.Kits[7] = new ContentNodeKit - { - ContentTypeId = 2, - Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 1, kit.Node.ParentContentId, DateTime.Now, 0), - DraftData = null, - PublishedData = new ContentData - { - Name = kit.PublishedData.Name, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = DateTime.Now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - } - }; - - kit = _source.Kits[8]; - _source.Kits[8] = new ContentNodeKit - { - ContentTypeId = 2, - Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 3, kit.Node.ParentContentId, DateTime.Now, 0), - DraftData = null, - PublishedData = new ContentData - { - Name = kit.PublishedData.Name, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = DateTime.Now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - } - }; - - kit = _source.Kits[9]; - _source.Kits[9] = new ContentNodeKit - { - ContentTypeId = 2, - Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 2, kit.Node.ParentContentId, DateTime.Now, 0), - DraftData = null, - PublishedData = new ContentData - { - Name = kit.PublishedData.Name, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = DateTime.Now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - } - }; + var kit = NuCacheContentService.ContentKits[7]; + NuCacheContentService.ContentKits[7] = ContentNodeKitBuilder.CreateWithContent( + contentTypeId: _contentTypeInvariant.Id, + id: kit.Node.Id, path: kit.Node.Path, sortOrder: 1, level: kit.Node.Level, parentContentId: kit.Node.ParentContentId, + draftData: null, + publishedData: ContentDataBuilder.CreateBasic(kit.PublishedData.Name)); + + kit = NuCacheContentService.ContentKits[8]; + NuCacheContentService.ContentKits[8] = ContentNodeKitBuilder.CreateWithContent( + contentTypeId: _contentTypeInvariant.Id, + id: kit.Node.Id, path: kit.Node.Path, sortOrder: 3, level: kit.Node.Level, parentContentId: kit.Node.ParentContentId, + draftData: null, + publishedData: ContentDataBuilder.CreateBasic(kit.PublishedData.Name)); + + kit = NuCacheContentService.ContentKits[9]; + NuCacheContentService.ContentKits[9] = ContentNodeKitBuilder.CreateWithContent( + contentTypeId: _contentTypeInvariant.Id, + id: kit.Node.Id, path: kit.Node.Path, sortOrder: 2, level: kit.Node.Level, parentContentId: kit.Node.ParentContentId, + draftData: null, + publishedData: ContentDataBuilder.CreateBasic(kit.PublishedData.Name)); // notify - _snapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(kit.Node.ParentContentId, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _); + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(kit.Node.ParentContentId, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _); // changes that *I* make are immediately visible on the current snapshot - var documents = snapshot.Content.GetById(kit.Node.ParentContentId).Children(_variationAccesor).ToArray(); + var documents = snapshot.Content.GetById(kit.Node.ParentContentId).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N7", "N9", "N8"); } [Test] public void MoveTest() { - Init(GetInvariantKits); + InitializedCache(GetInvariantKits(), _contentTypes); // get snapshot - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + var snapshot = GetPublishedSnapshot(); // do some changes - var kit = _source.Kits[4]; - _source.Kits[4] = new ContentNodeKit - { - ContentTypeId = 2, - Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 2, kit.Node.ParentContentId, DateTime.Now, 0), - DraftData = null, - PublishedData = new ContentData - { - Name = kit.PublishedData.Name, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = DateTime.Now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - } - }; - - kit = _source.Kits[5]; - _source.Kits[5] = new ContentNodeKit - { - ContentTypeId = 2, - Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 3, kit.Node.ParentContentId, DateTime.Now, 0), - DraftData = null, - PublishedData = new ContentData - { - Name = kit.PublishedData.Name, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = DateTime.Now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - } - }; - - kit = _source.Kits[6]; - _source.Kits[6] = new ContentNodeKit - { - ContentTypeId = 2, - Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, kit.Node.Path, 4, kit.Node.ParentContentId, DateTime.Now, 0), - DraftData = null, - PublishedData = new ContentData - { - Name = kit.PublishedData.Name, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = DateTime.Now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - } - }; - - kit = _source.Kits[7]; - _source.Kits[7] = new ContentNodeKit - { - ContentTypeId = 2, - Node = new ContentNode(kit.Node.Id, Guid.NewGuid(), kit.Node.Level, "-1,1,7", 1, 1, DateTime.Now, 0), - DraftData = null, - PublishedData = new ContentData - { - Name = kit.PublishedData.Name, - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = DateTime.Now, - WriterId = 0, - Properties = new Dictionary(), - CultureInfos = new Dictionary() - } - }; + var kit = NuCacheContentService.ContentKits[4]; + NuCacheContentService.ContentKits[4] = ContentNodeKitBuilder.CreateWithContent( + contentTypeId: _contentTypeInvariant.Id, + id: kit.Node.Id, path: kit.Node.Path, sortOrder: 2, level: kit.Node.Level, parentContentId: kit.Node.ParentContentId, + draftData: null, + publishedData: ContentDataBuilder.CreateBasic(kit.PublishedData.Name)); + + kit = NuCacheContentService.ContentKits[5]; + NuCacheContentService.ContentKits[5] = ContentNodeKitBuilder.CreateWithContent( + contentTypeId: _contentTypeInvariant.Id, + id: kit.Node.Id, path: kit.Node.Path, sortOrder: 3, level: kit.Node.Level, parentContentId: kit.Node.ParentContentId, + draftData: null, + publishedData: ContentDataBuilder.CreateBasic(kit.PublishedData.Name)); + + kit = NuCacheContentService.ContentKits[6]; + NuCacheContentService.ContentKits[6] = ContentNodeKitBuilder.CreateWithContent( + contentTypeId: _contentTypeInvariant.Id, + id: kit.Node.Id, path: kit.Node.Path, sortOrder: 4, level: kit.Node.Level, parentContentId: kit.Node.ParentContentId, + draftData: null, + publishedData: ContentDataBuilder.CreateBasic(kit.PublishedData.Name)); + ; + + kit = NuCacheContentService.ContentKits[7]; + NuCacheContentService.ContentKits[7] = ContentNodeKitBuilder.CreateWithContent( + contentTypeId: _contentTypeInvariant.Id, + id: kit.Node.Id, path: "-1,1,7", sortOrder: 1, level: kit.Node.Level, parentContentId: 1, + draftData: null, + publishedData: ContentDataBuilder.CreateBasic(kit.PublishedData.Name)); // notify - _snapshotService.Notify(new[] + SnapshotService.Notify(new[] { // removal must come first new ContentCacheRefresher.JsonPayload(2, Guid.Empty, TreeChangeTypes.RefreshBranch), @@ -695,10 +424,10 @@ public void MoveTest() }, out _, out _); // changes that *I* make are immediately visible on the current snapshot - var documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); + var documents = snapshot.Content.GetById(1).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N7", "N4", "N5", "N6"); - documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(2).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N9", "N8"); Assert.AreEqual(1, snapshot.Content.GetById(7).Parent?.Id); @@ -712,7 +441,7 @@ public void Clear_Branch_Locked() var paths = new Dictionary { { -1, "-1" } }; - Init(() => new List + InitializedCache(new List { CreateInvariantKit(1, -1, 1, paths), // first level CreateInvariantKit(2, 1, 1, paths), // second level @@ -727,19 +456,18 @@ public void Clear_Branch_Locked() CreateInvariantKit(8, 5, 4, paths), CreateInvariantKit(9, 5, 5, paths), CreateInvariantKit(10, 5, 6, paths) - }); + }, _contentTypes); // get snapshot - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + var snapshot = GetPublishedSnapshot(); - var snapshotService = (PublishedSnapshotService)_snapshotService; + var snapshotService = (PublishedSnapshotService)SnapshotService; var contentStore = snapshotService.GetContentStore(); //This will set a flag to force creating a new Gen next time the store is locked (i.e. In Notify) contentStore.CreateSnapshot(); // notify - which ensures there are 2 generations in the cache meaning each LinkedNode has a Next value. - _snapshotService.Notify(new[] + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(4, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _); @@ -749,7 +477,7 @@ public void Clear_Branch_Locked() // to a child, we null out the .Value of the LinkedNode within the while loop because we didn't capture // this value before recursing. Assert.DoesNotThrow(() => - _snapshotService.Notify(new[] + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(4, Guid.Empty, TreeChangeTypes.RefreshBranch) }, out _, out _)); @@ -758,45 +486,45 @@ public void Clear_Branch_Locked() [Test] public void NestedVariationChildrenTest() { - Init(GetNestedVariantKits); + InitializedCache(GetNestedVariantKits(), _contentTypes); - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + // get snapshot + var snapshot = GetPublishedSnapshot(); //TEST with en-us variation context - _variationAccesor.VariationContext = new VariationContext("en-US"); + VariationContextAccessor.VariationContext = new VariationContext("en-US"); var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1-en-US"); - documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N4", "N7-en-US"); //Get the invariant and list children, there's a variation context so it should return invariant AND en-us variants - documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(4).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N10-en-US", "N11"); //Get the variant and list children, there's a variation context so it should return invariant AND en-us variants - documents = snapshot.Content.GetById(7).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(7).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N12-en-US", "N13"); //TEST with fr-fr variation context - _variationAccesor.VariationContext = new VariationContext("fr-FR"); + VariationContextAccessor.VariationContext = new VariationContext("fr-FR"); documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1-fr-FR"); - documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N4", "N7-fr-FR"); //Get the invariant and list children, there's a variation context so it should return invariant AND en-us variants - documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(4).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N10-fr-FR", "N11"); //Get the variant and list children, there's a variation context so it should return invariant AND en-us variants - documents = snapshot.Content.GetById(7).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(7).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N12-fr-FR", "N13"); //TEST specific cultures @@ -804,26 +532,26 @@ public void NestedVariationChildrenTest() documents = snapshot.Content.GetAtRoot("fr-FR").ToArray(); AssertDocuments(documents, "N1-fr-FR"); - documents = snapshot.Content.GetById(1).Children(_variationAccesor, "fr-FR").ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor, "fr-FR").ToArray(); AssertDocuments(documents, "N4", "N7-fr-FR"); //NOTE: Returns invariant, this is expected - documents = snapshot.Content.GetById(1).Children(_variationAccesor, "").ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor, "").ToArray(); AssertDocuments(documents, "N4"); //Only returns invariant since that is what was requested - documents = snapshot.Content.GetById(4).Children(_variationAccesor, "fr-FR").ToArray(); + documents = snapshot.Content.GetById(4).Children(VariationContextAccessor, "fr-FR").ToArray(); AssertDocuments(documents, "N10-fr-FR", "N11"); //NOTE: Returns invariant, this is expected - documents = snapshot.Content.GetById(4).Children(_variationAccesor, "").ToArray(); + documents = snapshot.Content.GetById(4).Children(VariationContextAccessor, "").ToArray(); AssertDocuments(documents, "N11"); //Only returns invariant since that is what was requested - documents = snapshot.Content.GetById(7).Children(_variationAccesor, "fr-FR").ToArray(); + documents = snapshot.Content.GetById(7).Children(VariationContextAccessor, "fr-FR").ToArray(); AssertDocuments(documents, "N12-fr-FR", "N13"); //NOTE: Returns invariant, this is expected - documents = snapshot.Content.GetById(7).Children(_variationAccesor, "").ToArray(); + documents = snapshot.Content.GetById(7).Children(VariationContextAccessor, "").ToArray(); AssertDocuments(documents, "N13"); //Only returns invariant since that is what was requested //TEST without variation context // This will actually convert the culture to "" which will be invariant since that's all it will know how to do // This will return a NULL name for culture specific entities because there is no variation context - _variationAccesor.VariationContext = null; + VariationContextAccessor.VariationContext = null; documents = snapshot.Content.GetAtRoot().ToArray(); //will return nothing because there's only variant at root @@ -832,72 +560,72 @@ public void NestedVariationChildrenTest() documents = snapshot.Content.GetAtRoot("fr-FR").ToArray(); Assert.AreEqual(1, documents.Length); - documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N4"); //Get the invariant and list children - documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(4).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N11"); //Get the variant and list children - documents = snapshot.Content.GetById(7).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(7).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N13"); } [Test] public void VariantChildrenTest() { - Init(GetVariantKits); + InitializedCache(GetVariantKits(), _contentTypes); - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + // get snapshot + var snapshot = GetPublishedSnapshot(); - _variationAccesor.VariationContext = new VariationContext("en-US"); + VariationContextAccessor.VariationContext = new VariationContext("en-US"); var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1-en-US", "N2-en-US", "N3-en-US"); - documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N4-en-US", "N5-en-US", "N6-en-US"); - documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(2).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N9-en-US", "N8-en-US", "N7-en-US"); - documents = snapshot.Content.GetById(3).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(3).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N10-en-US"); - documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(4).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N11-en-US", "N12-en-US"); - documents = snapshot.Content.GetById(10).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(10).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents); - _variationAccesor.VariationContext = new VariationContext("fr-FR"); + VariationContextAccessor.VariationContext = new VariationContext("fr-FR"); documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1-fr-FR", "N3-fr-FR"); - documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N4-fr-FR", "N6-fr-FR"); - documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(2).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N9-fr-FR", "N7-fr-FR"); - documents = snapshot.Content.GetById(3).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(3).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N10-fr-FR"); - documents = snapshot.Content.GetById(4).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(4).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N12-fr-FR"); - documents = snapshot.Content.GetById(10).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(10).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents); - documents = snapshot.Content.GetById(1).Children(_variationAccesor, "*").ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor, "*").ToArray(); AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR"); AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US"); - documents = snapshot.Content.GetById(1).Children(_variationAccesor, "en-US").ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor, "en-US").ToArray(); AssertDocuments(documents, "N4-fr-FR", null, "N6-fr-FR"); AssertDocuments("en-US", documents, "N4-en-US", "N5-en-US", "N6-en-US"); @@ -909,32 +637,32 @@ public void VariantChildrenTest() documents = snapshot.Content.GetAtRoot("*").ToArray(); AssertDocuments(documents, "N1-fr-FR", null, "N3-fr-FR"); - documents = snapshot.Content.GetById(1).DescendantsOrSelf(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(1).DescendantsOrSelf(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N1-fr-FR", "N4-fr-FR", "N12-fr-FR", "N6-fr-FR"); - documents = snapshot.Content.GetById(1).DescendantsOrSelf(_variationAccesor, "*").ToArray(); + documents = snapshot.Content.GetById(1).DescendantsOrSelf(VariationContextAccessor, "*").ToArray(); AssertDocuments(documents, "N1-fr-FR", "N4-fr-FR", null /*11*/, "N12-fr-FR", null /*5*/, "N6-fr-FR"); } [Test] public void RemoveTest() { - Init(GetInvariantKits); + InitializedCache(GetInvariantKits(), _contentTypes); - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + // get snapshot + var snapshot = GetPublishedSnapshot(); var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2", "N3"); - documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N4", "N5", "N6"); - documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(2).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N9", "N8", "N7"); // notify - _snapshotService.Notify(new[] + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(3, Guid.Empty, TreeChangeTypes.Remove), // remove last new ContentCacheRefresher.JsonPayload(5, Guid.Empty, TreeChangeTypes.Remove), // remove middle @@ -944,14 +672,14 @@ public void RemoveTest() documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2"); - documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N4", "N6"); - documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(2).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N8", "N7"); // notify - _snapshotService.Notify(new[] + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.Remove), // remove first new ContentCacheRefresher.JsonPayload(8, Guid.Empty, TreeChangeTypes.Remove), // remove @@ -961,19 +689,19 @@ public void RemoveTest() documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N2"); - documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(2).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents); } [Test] public void UpdateTest() { - Init(GetInvariantKits); + InitializedCache(GetInvariantKits(), _contentTypes); - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + // get snapshot + var snapshot = GetPublishedSnapshot(); - var snapshotService = (PublishedSnapshotService)_snapshotService; + var snapshotService = (PublishedSnapshotService)SnapshotService; var contentStore = snapshotService.GetContentStore(); var parentNodes = contentStore.Test.GetValues(1); @@ -984,14 +712,14 @@ public void UpdateTest() var documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2", "N3"); - documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N4", "N5", "N6"); - documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(2).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N9", "N8", "N7"); // notify - _snapshotService.Notify(new[] + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshBranch), new ContentCacheRefresher.JsonPayload(2, Guid.Empty, TreeChangeTypes.RefreshNode), @@ -1009,10 +737,10 @@ public void UpdateTest() documents = snapshot.Content.GetAtRoot().ToArray(); AssertDocuments(documents, "N1", "N2", "N3"); - documents = snapshot.Content.GetById(1).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(1).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N4", "N5", "N6"); - documents = snapshot.Content.GetById(2).Children(_variationAccesor).ToArray(); + documents = snapshot.Content.GetById(2).Children(VariationContextAccessor).ToArray(); AssertDocuments(documents, "N9", "N8", "N7"); @@ -1021,12 +749,12 @@ public void UpdateTest() [Test] public void AtRootTest() { - Init(GetVariantWithDraftKits); + InitializedCache(GetVariantWithDraftKits(), _contentTypes); - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + // get snapshot + var snapshot = GetPublishedSnapshot(); - _variationAccesor.VariationContext = new VariationContext("en-US"); + VariationContextAccessor.VariationContext = new VariationContext("en-US"); // N2 is draft only @@ -1050,16 +778,16 @@ IEnumerable GetKits() yield return CreateInvariantKit(2, 1, 1, paths); } - Init(GetKits); + InitializedCache(GetKits(), _contentTypes); - var snapshotService = (PublishedSnapshotService)_snapshotService; + var snapshotService = (PublishedSnapshotService)SnapshotService; var contentStore = snapshotService.GetContentStore(); var parentNodes = contentStore.Test.GetValues(1); var parentNode = parentNodes[0]; AssertLinkedNode(parentNode.contentNode, -1, -1, -1, 2, 2); - _snapshotService.Notify(new[] + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(2, Guid.Empty, TreeChangeTypes.Remove) }, out _, out _); @@ -1089,9 +817,9 @@ IEnumerable GetKits() yield return CreateInvariantKit(4, 1, 3, paths); } - Init(GetKits); + InitializedCache(GetKits(), _contentTypes); - var snapshotService = (PublishedSnapshotService)_snapshotService; + var snapshotService = (PublishedSnapshotService)SnapshotService; var contentStore = snapshotService.GetContentStore(); Assert.AreEqual(1, contentStore.Test.LiveGen); @@ -1118,7 +846,7 @@ IEnumerable GetKits() Assert.IsFalse(contentStore.Test.NextGen); - _snapshotService.Notify(new[] + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(3, Guid.Empty, TreeChangeTypes.Remove) //remove middle child }, out _, out _); @@ -1169,9 +897,9 @@ IEnumerable GetKits() yield return CreateInvariantKit(40, 1, 3, paths); } - Init(GetKits); + InitializedCache(GetKits(), _contentTypes); - var snapshotService = (PublishedSnapshotService)_snapshotService; + var snapshotService = (PublishedSnapshotService)SnapshotService; var contentStore = snapshotService.GetContentStore(); Assert.AreEqual(1, contentStore.Test.LiveGen); @@ -1186,7 +914,7 @@ IEnumerable GetKits() Assert.IsFalse(contentStore.Test.NextGen); - _snapshotService.Notify(new[] + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, TreeChangeTypes.RefreshNode) }, out _, out _); @@ -1241,12 +969,12 @@ IEnumerable GetKits() } //init with all published - Init(GetKits); + InitializedCache(GetKits(), _contentTypes); - var snapshotService = (PublishedSnapshotService)_snapshotService; + var snapshotService = (PublishedSnapshotService)SnapshotService; var contentStore = snapshotService.GetContentStore(); - var rootKit = _source.Kits[1].Clone(PublishedModelFactory); + var rootKit = NuCacheContentService.ContentKits[1].Clone(PublishedModelFactory); void ChangePublishFlagOfRoot(bool published, int assertGen, TreeChangeTypes changeType) { @@ -1256,12 +984,13 @@ void ChangePublishFlagOfRoot(bool published, int assertGen, TreeChangeTypes chan Assert.IsFalse(contentStore.Test.NextGen); //Change the root publish flag - var kit = rootKit.Clone(PublishedModelFactory); - kit.DraftData = published ? null : kit.PublishedData; - kit.PublishedData = published ? kit.PublishedData : null; - _source.Kits[1] = kit; + var kit = rootKit.Clone( + PublishedModelFactory, + published ? null : rootKit.PublishedData, + published ? rootKit.PublishedData : null); + NuCacheContentService.ContentKits[1] = kit; - _snapshotService.Notify(new[] + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(1, Guid.Empty, changeType) }, out _, out _); @@ -1311,9 +1040,9 @@ IEnumerable GetKits() yield return CreateInvariantKit(4, 1, 3, paths); } - Init(GetKits); + InitializedCache(GetKits(), _contentTypes); - var snapshotService = (PublishedSnapshotService)_snapshotService; + var snapshotService = (PublishedSnapshotService)SnapshotService; var contentStore = snapshotService.GetContentStore(); Assert.AreEqual(1, contentStore.Test.LiveGen); @@ -1340,7 +1069,7 @@ IEnumerable GetKits() Assert.IsFalse(contentStore.Test.NextGen); - _snapshotService.Notify(new[] + SnapshotService.Notify(new[] { new ContentCacheRefresher.JsonPayload(3, Guid.Empty, TreeChangeTypes.RefreshBranch) //remove middle child }, out _, out _); @@ -1371,13 +1100,13 @@ IEnumerable GetKits() public void MultipleCacheIteration() { //see https://github.com/umbraco/Umbraco-CMS/issues/7798 - Init(GetInvariantKits); - var snapshot = this._snapshotService.CreatePublishedSnapshot(previewToken: null); - _snapshotAccessor.PublishedSnapshot = snapshot; + InitializedCache(GetInvariantKits(), _contentTypes); + var snapshot = GetPublishedSnapshot(); var items = snapshot.Content.GetByXPath("/root/itype"); Assert.AreEqual(items.Count(), items.Count()); } + private void AssertLinkedNode(ContentNode node, int parent, int prevSibling, int nextSibling, int firstChild, int lastChild) { Assert.AreEqual(parent, node.ParentContentId); @@ -1398,7 +1127,7 @@ private void AssertDocuments(string culture, IPublishedContent[] documents, para { Assert.AreEqual(names.Length, documents.Length); for (var i = 0; i < names.Length; i++) - Assert.AreEqual(names[i], documents[i].Name(_variationAccesor, culture)); + Assert.AreEqual(names[i], documents[i].Name(VariationContextAccessor, culture)); } } } diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceContentTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceContentTests.cs new file mode 100644 index 000000000000..00c3e965c6d2 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedSnapshotServiceContentTests.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using Moq; +using NUnit.Framework; +using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PublishedCache; +using Umbraco.Cms.Core.Services.Changes; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; +using Umbraco.Cms.Tests.Common.Builders; +using Umbraco.Cms.Tests.Common.Builders.Extensions; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; +using Umbraco.Extensions; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache +{ + + + [TestFixture] + public class PublishedSnapshotServiceContentTests : PublishedSnapshotServiceTestBase + { + private ContentType _contentType; + private PropertyType _propertyType; + + [SetUp] + public override void Setup() + { + base.Setup(); + + _propertyType = new PropertyType(TestHelper.ShortStringHelper, "Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.Culture }; + _contentType = new ContentType(TestHelper.ShortStringHelper, -1) { Id = 2, Alias = "alias-ct", Variations = ContentVariation.Culture }; + _contentType.AddPropertyType(_propertyType); + + var contentTypes = new[] + { + _contentType + }; + + InitializedCache(new[] { CreateKit() }, contentTypes); + } + + private ContentNodeKit CreateKit() + { + var draftData = new ContentDataBuilder() + .WithName("It Works2!") + .WithPublished(false) + .WithProperties(new Dictionary + { + ["prop"] = new[] + { + new PropertyData { Culture = "", Segment = "", Value = "val2" }, + new PropertyData { Culture = "fr-FR", Segment = "", Value = "val-fr2" }, + new PropertyData { Culture = "en-UK", Segment = "", Value = "val-uk2" }, + new PropertyData { Culture = "dk-DA", Segment = "", Value = "val-da2" }, + new PropertyData { Culture = "de-DE", Segment = "", Value = "val-de2" } + } + }) + .WithCultureInfos(new Dictionary + { + // draft data = everything, and IsDraft indicates what's edited + ["fr-FR"] = new CultureVariation { Name = "name-fr2", IsDraft = true, Date = new DateTime(2018, 01, 03, 01, 00, 00) }, + ["en-UK"] = new CultureVariation { Name = "name-uk2", IsDraft = true, Date = new DateTime(2018, 01, 04, 01, 00, 00) }, + ["dk-DA"] = new CultureVariation { Name = "name-da2", IsDraft = true, Date = new DateTime(2018, 01, 05, 01, 00, 00) }, + ["de-DE"] = new CultureVariation { Name = "name-de1", IsDraft = false, Date = new DateTime(2018, 01, 02, 01, 00, 00) } + }) + .Build(); + + var publishedData = new ContentDataBuilder() + .WithName("It Works1!") + .WithPublished(true) + .WithProperties(new Dictionary + { + ["prop"] = new[] + { + new PropertyData { Culture = "", Segment = "", Value = "val1" }, + new PropertyData { Culture = "fr-FR", Segment = "", Value = "val-fr1" }, + new PropertyData { Culture = "en-UK", Segment = "", Value = "val-uk1" } + } + }) + .WithCultureInfos(new Dictionary + { + // published data = only what's actually published, and IsDraft has to be false + ["fr-FR"] = new CultureVariation { Name = "name-fr1", IsDraft = false, Date = new DateTime(2018, 01, 01, 01, 00, 00) }, + ["en-UK"] = new CultureVariation { Name = "name-uk1", IsDraft = false, Date = new DateTime(2018, 01, 02, 01, 00, 00) }, + ["de-DE"] = new CultureVariation { Name = "name-de1", IsDraft = false, Date = new DateTime(2018, 01, 02, 01, 00, 00) } + }) + .Build(); + + var kit = ContentNodeKitBuilder.CreateWithContent( + 2, + 1, "-1,1", 0, + draftData: draftData, + publishedData: publishedData); + + return kit; + } + + [Test] + public void Verifies_Variant_Data() + { + // this test implements a full standalone NuCache (based upon a test IDataSource, does not + // use any local db files, does not rely on any database) - and tests variations + + // get a snapshot, get a published content + IPublishedSnapshot snapshot = GetPublishedSnapshot(); + IPublishedContent publishedContent = snapshot.Content.GetById(1); + + Assert.IsNotNull(publishedContent); + Assert.AreEqual("val1", publishedContent.Value(Mock.Of(), "prop")); + Assert.AreEqual("val-fr1", publishedContent.Value(Mock.Of(), "prop", "fr-FR")); + Assert.AreEqual("val-uk1", publishedContent.Value(Mock.Of(), "prop", "en-UK")); + + Assert.IsNull(publishedContent.Name(VariationContextAccessor)); // no invariant name for varying content + Assert.AreEqual("name-fr1", publishedContent.Name(VariationContextAccessor, "fr-FR")); + Assert.AreEqual("name-uk1", publishedContent.Name(VariationContextAccessor, "en-UK")); + + var draftContent = snapshot.Content.GetById(true, 1); + Assert.AreEqual("val2", draftContent.Value(Mock.Of(), "prop")); + Assert.AreEqual("val-fr2", draftContent.Value(Mock.Of(), "prop", "fr-FR")); + Assert.AreEqual("val-uk2", draftContent.Value(Mock.Of(), "prop", "en-UK")); + + Assert.IsNull(draftContent.Name(VariationContextAccessor)); // no invariant name for varying content + Assert.AreEqual("name-fr2", draftContent.Name(VariationContextAccessor, "fr-FR")); + Assert.AreEqual("name-uk2", draftContent.Name(VariationContextAccessor, "en-UK")); + + // now french is default + VariationContextAccessor.VariationContext = new VariationContext("fr-FR"); + Assert.AreEqual("val-fr1", publishedContent.Value(Mock.Of(), "prop")); + Assert.AreEqual("name-fr1", publishedContent.Name(VariationContextAccessor)); + Assert.AreEqual(new DateTime(2018, 01, 01, 01, 00, 00), publishedContent.CultureDate(VariationContextAccessor)); + + // now uk is default + VariationContextAccessor.VariationContext = new VariationContext("en-UK"); + Assert.AreEqual("val-uk1", publishedContent.Value(Mock.Of(), "prop")); + Assert.AreEqual("name-uk1", publishedContent.Name(VariationContextAccessor)); + Assert.AreEqual(new DateTime(2018, 01, 02, 01, 00, 00), publishedContent.CultureDate(VariationContextAccessor)); + + // invariant needs to be retrieved explicitly, when it's not default + Assert.AreEqual("val1", publishedContent.Value(Mock.Of(), "prop", culture: "")); + + // but, + // if the content type / property type does not vary, then it's all invariant again + // modify the content type and property type, notify the snapshot service + _contentType.Variations = ContentVariation.Nothing; + _propertyType.Variations = ContentVariation.Nothing; + SnapshotService.Notify(new[] { new ContentTypeCacheRefresher.JsonPayload("IContentType", publishedContent.ContentType.Id, ContentTypeChangeTypes.RefreshMain) }); + + // get a new snapshot (nothing changed in the old one), get the published content again + var anotherSnapshot = SnapshotService.CreatePublishedSnapshot(previewToken: null); + var againContent = anotherSnapshot.Content.GetById(1); + + Assert.AreEqual(ContentVariation.Nothing, againContent.ContentType.Variations); + Assert.AreEqual(ContentVariation.Nothing, againContent.ContentType.GetPropertyType("prop").Variations); + + // now, "no culture" means "invariant" + Assert.AreEqual("It Works1!", againContent.Name(VariationContextAccessor)); + Assert.AreEqual("val1", againContent.Value(Mock.Of(), "prop")); + } + + [Test] + public void Verifies_Published_And_Draft_Content() + { + // get the published published content + var snapshot = GetPublishedSnapshot(); + var c1 = snapshot.Content.GetById(1); + + // published content = nothing is draft here + Assert.IsFalse(c1.IsDraft("fr-FR")); + Assert.IsFalse(c1.IsDraft("en-UK")); + Assert.IsFalse(c1.IsDraft("dk-DA")); + Assert.IsFalse(c1.IsDraft("de-DE")); + + // and only those with published name, are published + Assert.IsTrue(c1.IsPublished("fr-FR")); + Assert.IsTrue(c1.IsPublished("en-UK")); + Assert.IsFalse(c1.IsDraft("dk-DA")); + Assert.IsTrue(c1.IsPublished("de-DE")); + + // get the draft published content + var c2 = snapshot.Content.GetById(true, 1); + + // draft content = we have drafts + Assert.IsTrue(c2.IsDraft("fr-FR")); + Assert.IsTrue(c2.IsDraft("en-UK")); + Assert.IsTrue(c2.IsDraft("dk-DA")); + Assert.IsFalse(c2.IsDraft("de-DE")); // except for the one that does not + + // and only those with published name, are published + Assert.IsTrue(c2.IsPublished("fr-FR")); + Assert.IsTrue(c2.IsPublished("en-UK")); + Assert.IsFalse(c2.IsPublished("dk-DA")); + Assert.IsTrue(c2.IsPublished("de-DE")); + } + + } +} diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/RootNodeTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/RootNodeTests.cs new file mode 100644 index 000000000000..1ec48759ad95 --- /dev/null +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/RootNodeTests.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; + +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache +{ + + [TestFixture] + public class RootNodeTests : PublishedSnapshotServiceTestBase + { + [SetUp] + public override void Setup() + { + base.Setup(); + + string xml = PublishedContentXml.TestWithDatabaseXml(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes); + } + + [Test] + public void PublishedContentHasNoRootNode() + { + var snapshot = GetPublishedSnapshot(); + + // there is no content node with ID -1 + var content = snapshot.Content.GetById(-1); + Assert.IsNull(content); + + // content at root has null parent + content = snapshot.Content.GetById(1046); + Assert.IsNotNull(content); + Assert.AreEqual(1, content.Level); + Assert.IsNull(content.Parent); + + // non-existing content is null + content = snapshot.Content.GetById(666); + Assert.IsNull(content); + } + + } +} diff --git a/tests/Umbraco.Tests/Routing/UrlRoutesTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/UrlRoutesTests.cs similarity index 68% rename from tests/Umbraco.Tests/Routing/UrlRoutesTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/UrlRoutesTests.cs index d315cc53a52b..a2cfe7d0e63f 100644 --- a/tests/Umbraco.Tests/Routing/UrlRoutesTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/UrlRoutesTests.cs @@ -1,25 +1,23 @@ -using System; +using System; +using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Tests.LegacyXmlPublishedCache; -using Umbraco.Tests.TestHelpers; +using Umbraco.Cms.Infrastructure.PublishedCache; +using Umbraco.Cms.Tests.Common.Published; +using Umbraco.Cms.Tests.UnitTests.TestHelpers; -namespace Umbraco.Tests.Routing +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.PublishedCache { // purpose: test the values returned by PublishedContentCache.GetRouteById // and .GetByRoute (no caching at all, just routing nice URLs) including all // the quirks due to hideTopLevelFromPath and backward compatibility. - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class UrlRoutesTests : TestWithDatabaseBase + public class UrlRoutesTests : PublishedSnapshotServiceTestBase { - #region Test Setup - - protected override string GetXmlContent(int templateId) - { - return @" + private static string GetXmlContent(int templateId) + => @" @@ -48,17 +46,6 @@ protected override string GetXmlContent(int templateId) "; - } - - protected override void Initialize() - { - base.Initialize(); - - if (FirstTestInFixture) - ServiceContext.ContentTypeService.Save(new ContentType(ShortStringHelper, -1) { Alias = "Doc", Name = "name" }); - } - - #endregion /* * Just so it's documented somewhere, as of jan. 2017, routes obey the following pseudo-code: @@ -193,11 +180,19 @@ compose path from parts [TestCase(2006, false, "/x/b/e")] public void GetRouteByIdNoHide(int id, bool hide, string expected) { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = hide }; + GlobalSettings.HideTopLevelNodeFromPath = hide; - var umbracoContext = GetUmbracoContext("/test", 0, globalSettings: globalSettings); - var cache = umbracoContext.Content as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + string xml = GetXmlContent(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + var cache = GetPublishedSnapshot().Content; var route = cache.GetRouteById(false, id); Assert.AreEqual(expected, route); @@ -216,12 +211,19 @@ public void GetRouteByIdNoHide(int id, bool hide, string expected) [TestCase(2006, true, "/b/e")] // risky! public void GetRouteByIdHide(int id, bool hide, string expected) { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = hide }; + GlobalSettings.HideTopLevelNodeFromPath = hide; + + string xml = GetXmlContent(1234); - var snapshotService = CreatePublishedSnapshotService(globalSettings); - var umbracoContext = GetUmbracoContext("/test", 0, globalSettings: globalSettings, snapshotService: snapshotService); - var cache = umbracoContext.Content as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + var cache = GetPublishedSnapshot().Content; var route = cache.GetRouteById(false, id); Assert.AreEqual(expected, route); @@ -230,27 +232,23 @@ public void GetRouteByIdHide(int id, bool hide, string expected) [Test] public void GetRouteByIdCache() { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; - var snapshotService = CreatePublishedSnapshotService(globalSettings); - var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings, snapshotService: snapshotService); - var cache = umbracoContext.Content as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + string xml = GetXmlContent(1234); - var route = cache.GetRouteById(false, 1000); - Assert.AreEqual("/a", route); + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); - // GetRouteById registers a non-trusted route, which is cached for - // id -> route queries (fast GetUrl) but *not* for route -> id - // queries (safe inbound routing) + InitializedCache(kits, contentTypes, dataTypes: dataTypes); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual(1, cachedRoutes.Count); - Assert.IsTrue(cachedRoutes.ContainsKey(1000)); - Assert.AreEqual("/a", cachedRoutes[1000]); + var cache = GetPublishedSnapshot().Content; - var cachedIds = cache.RoutesCache.GetCachedIds(); - Assert.AreEqual(0, cachedIds.Count); + + var route = cache.GetRouteById(false, 1000); + Assert.AreEqual("/a", route); } [TestCase("/", false, 1000)] @@ -261,12 +259,19 @@ public void GetRouteByIdCache() [TestCase("/x", false, 2000)] public void GetByRouteNoHide(string route, bool hide, int expected) { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = hide }; + GlobalSettings.HideTopLevelNodeFromPath = hide; + + string xml = GetXmlContent(1234); - var snapshotService = CreatePublishedSnapshotService(globalSettings); - var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings, snapshotService: snapshotService); - var cache = umbracoContext.Content as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); + + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + var cache = GetPublishedSnapshot().Content; const bool preview = false; // make sure we don't cache - but HOW? should be some sort of switch?! var content = cache.GetByRoute(preview, route); @@ -292,12 +297,19 @@ public void GetByRouteNoHide(string route, bool hide, int expected) [TestCase("/b/c", true, 1002)] // (hence the 2005 collision) public void GetByRouteHide(string route, bool hide, int expected) { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = hide }; + GlobalSettings.HideTopLevelNodeFromPath = hide; + + string xml = GetXmlContent(1234); + + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); - var snapshotService = CreatePublishedSnapshotService(globalSettings); - var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings, snapshotService: snapshotService); - var cache = umbracoContext.Content as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + InitializedCache(kits, contentTypes, dataTypes: dataTypes); + + var cache = GetPublishedSnapshot().Content; const bool preview = false; // make sure we don't cache - but HOW? should be some sort of switch?! var content = cache.GetByRoute(preview, route); @@ -315,30 +327,23 @@ public void GetByRouteHide(string route, bool hide, int expected) [Test] public void GetByRouteCache() { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; + GlobalSettings.HideTopLevelNodeFromPath = false; - var snapshotService = CreatePublishedSnapshotService(globalSettings); - var umbracoContext = GetUmbracoContext("/test", 0, globalSettings:globalSettings, snapshotService:snapshotService); - var cache = umbracoContext.Content as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); + string xml = GetXmlContent(1234); - var content = cache.GetByRoute(false, "/a/b/c"); - Assert.IsNotNull(content); - Assert.AreEqual(1002, content.Id); + IEnumerable kits = PublishedContentXmlAdapter.GetContentNodeKits( + xml, + TestHelper.ShortStringHelper, + out ContentType[] contentTypes, + out DataType[] dataTypes).ToList(); - // GetByRoute registers a trusted route, which is cached both for - // id -> route queries (fast GetUrl) and for route -> id queries - // (fast inbound routing) + InitializedCache(kits, contentTypes, dataTypes: dataTypes); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual(1, cachedRoutes.Count); - Assert.IsTrue(cachedRoutes.ContainsKey(1002)); - Assert.AreEqual("/a/b/c", cachedRoutes[1002]); + var cache = GetPublishedSnapshot().Content; - var cachedIds = cache.RoutesCache.GetCachedIds(); - Assert.AreEqual(1, cachedIds.Count); - Assert.IsTrue(cachedIds.ContainsKey("/a/b/c")); - Assert.AreEqual(1002, cachedIds["/a/b/c"]); + var content = cache.GetByRoute(false, "/a/b/c"); + Assert.IsNotNull(content); + Assert.AreEqual(1002, content.Id); } } } diff --git a/tests/Umbraco.Tests/Serialization/AutoInterningStringConverterTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Serialization/AutoInterningStringConverterTests.cs similarity index 87% rename from tests/Umbraco.Tests/Serialization/AutoInterningStringConverterTests.cs rename to tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Serialization/AutoInterningStringConverterTests.cs index f83ea940c99c..472536351b05 100644 --- a/tests/Umbraco.Tests/Serialization/AutoInterningStringConverterTests.cs +++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Serialization/AutoInterningStringConverterTests.cs @@ -1,14 +1,11 @@ -using Newtonsoft.Json; +using Newtonsoft.Json; using NUnit.Framework; using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml.Serialization; -using Umbraco.Core.Serialization; +using Umbraco.Cms.Infrastructure.Serialization; -namespace Umbraco.Tests.Serialization +namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Serialization { [TestFixture] public class AutoInterningStringConverterTests @@ -61,7 +58,7 @@ public class Test public string Name { get; set; } [JsonConverter(typeof(AutoInterningStringKeyCaseInsensitiveDictionaryConverter))] - public Dictionary Values = new Dictionary(); + public Dictionary Values { get; set; } = new Dictionary(); } } } diff --git a/tests/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/tests/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs deleted file mode 100644 index bec5bbbb92a8..000000000000 --- a/tests/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System.Linq; -using System.Xml; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Tests.Common; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Tests.LegacyXmlPublishedCache; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web; - -namespace Umbraco.Tests.Cache.PublishedCache -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class PublishContentCacheTests : BaseWebTest - { - private FakeHttpContextFactory _httpContextFactory; - private IUmbracoContext _umbracoContext; - private IPublishedContentCache _cache; - private XmlDocument _xml; - - private string GetXml() - { - return @" - - -]> - - - - - - - - - - - - - - -"; - } - - protected override void Initialize() - { - base.Initialize(); - - _httpContextFactory = new FakeHttpContextFactory("~/Home"); - - var globalSettings = new GlobalSettings(); - var umbracoContextAccessor = Factory.GetRequiredService(); - - _xml = new XmlDocument(); - _xml.LoadXml(GetXml()); - var xmlStore = new XmlStore(() => _xml, null, null, null, HostingEnvironment); - var appCache = new DictionaryAppCache(); - var domainCache = new DomainCache(Mock.Of(), DefaultCultureAccessor); - var publishedShapshot = new PublishedSnapshot( - new PublishedContentCache(xmlStore, domainCache, appCache, globalSettings, ContentTypesCache, null, VariationContextAccessor, null), - new PublishedMediaCache(xmlStore, Mock.Of(), Mock.Of(), appCache, ContentTypesCache, Factory.GetRequiredService(), umbracoContextAccessor, VariationContextAccessor), - new PublishedMemberCache(ContentTypesCache, VariationContextAccessor), - domainCache); - var publishedSnapshotService = new Mock(); - publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedShapshot); - - var httpContext = _httpContextFactory.HttpContext; - var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext); - _umbracoContext = new UmbracoContext( - httpContextAccessor, - publishedSnapshotService.Object, - Mock.Of(), - globalSettings, - HostingEnvironment, - new TestVariationContextAccessor(), - UriUtility, - new AspNetCookieManager(httpContextAccessor)); - - _cache = _umbracoContext.Content; - } - - [Test] - public void Has_Content() - { - Assert.IsTrue(_cache.HasContent()); - } - - - [Test] - public void Get_Root_Docs() - { - var result = _cache.GetAtRoot(); - Assert.AreEqual(2, result.Count()); - Assert.AreEqual(1046, result.ElementAt(0).Id); - Assert.AreEqual(1172, result.ElementAt(1).Id); - } - - - [TestCase("/", 1046)] - [TestCase("/home", 1046)] - [TestCase("/Home", 1046)] //test different cases - [TestCase("/home/sub1", 1173)] - [TestCase("/Home/sub1", 1173)] - [TestCase("/home/Sub1", 1173)] //test different cases - [TestCase("/home/Sub'Apostrophe", 1177)] - public void Get_Node_By_Route(string route, int nodeId) - { - var result = _cache.GetByRoute(route, false); - Assert.IsNotNull(result); - Assert.AreEqual(nodeId, result.Id); - } - - - - [TestCase("/", 1046)] - [TestCase("/sub1", 1173)] - [TestCase("/Sub1", 1173)] - public void Get_Node_By_Route_Hiding_Top_Level_Nodes(string route, int nodeId) - { - var result = _cache.GetByRoute(route, true); - Assert.IsNotNull(result); - Assert.AreEqual(nodeId, result.Id); - } - } -} diff --git a/tests/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/tests/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs deleted file mode 100644 index 416035e0e49a..000000000000 --- a/tests/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ /dev/null @@ -1,413 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Xml; -using Examine; -using Microsoft.Extensions.DependencyInjection; -using NUnit.Framework; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.Membership; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.LegacyXmlPublishedCache; -using Umbraco.Tests.PublishedContent; -using Umbraco.Tests.TestHelpers; -using Constants = Umbraco.Cms.Core.Constants; -using Current = Umbraco.Web.Composing.Current; - -namespace Umbraco.Tests.Cache.PublishedCache -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class PublishMediaCacheTests : BaseWebTest - { - private Dictionary _mediaTypes; - private int _testWriterAndCreatorId; - - private IUmbracoContextAccessor _umbracoContextAccessor; - protected override void Compose() - { - base.Compose(); - - Builder.WithCollectionBuilder() - .Clear() - .Append(); - - _umbracoContextAccessor = Current.UmbracoContextAccessor; - } - - protected override void Initialize() - { - base.Initialize(); - var type = new AutoPublishedContentType(Guid.NewGuid(), 22, "myType", new PublishedPropertyType[] { }); - var image = new AutoPublishedContentType(Guid.NewGuid(), 23, "Image", new PublishedPropertyType[] { }); - var testMediaType = new AutoPublishedContentType(Guid.NewGuid(), 24, "TestMediaType", new PublishedPropertyType[] { }); - _mediaTypes = new Dictionary - { - { type.Alias, type }, - { image.Alias, image }, - { testMediaType.Alias, testMediaType } - }; - ContentTypesCache.GetPublishedContentTypeByAlias = alias => _mediaTypes[alias]; - - _testWriterAndCreatorId = ServiceContext.UserService.CreateUserWithIdentity("Shannon", "test").Id; - } - - private IMediaType MakeNewMediaType(IUser user, string text, int parentId = -1) - { - var mt = new MediaType(ShortStringHelper, parentId) { Name = text, Alias = text, Thumbnail = "icon-folder", Icon = "icon-folder" }; - ServiceContext.MediaTypeService.Save(mt); - return mt; - } - - private IMedia MakeNewMedia(string name, IMediaType mediaType, IUser user, int parentId) - { - var m = ServiceContext.MediaService.CreateMediaWithIdentity(name, parentId, mediaType.Alias); - return m; - } - - //NOTE: This is "Without_Examine" too - [Test] - public void Get_Root_Docs() - { - var user = ServiceContext.UserService.GetUserById(0); - var mType = MakeNewMediaType(user, "TestMediaType"); - var mRoot1 = MakeNewMedia("MediaRoot1", mType, user, -1); - var mRoot2 = MakeNewMedia("MediaRoot2", mType, user, -1); - var mChild1 = MakeNewMedia("Child1", mType, user, mRoot1.Id); - var mChild2 = MakeNewMedia("Child2", mType, user, mRoot2.Id); - - var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(new XmlStore((XmlDocument) null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService(), VariationContextAccessor); - var roots = cache.GetAtRoot(); - Assert.AreEqual(2, roots.Count()); - Assert.IsTrue(roots.Select(x => x.Id).ContainsAll(new[] {mRoot1.Id, mRoot2.Id})); - - } - - [Test] - public void Get_Item_Without_Examine() - { - var user = ServiceContext.UserService.GetUserById(0); - var mType = MakeNewMediaType(user, "TestMediaType"); - _mediaTypes[mType.Alias] = new PublishedContentType(mType, null); - var mRoot = MakeNewMedia("MediaRoot", mType, user, -1); - var mChild1 = MakeNewMedia("Child1", mType, user, mRoot.Id); - - //var publishedMedia = PublishedMediaTests.GetNode(mRoot.Id, GetUmbracoContext("/test", 1234)); - var umbracoContext = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService(), VariationContextAccessor); - var publishedMedia = cache.GetById(mRoot.Id); - Assert.IsNotNull(publishedMedia); - - Assert.AreEqual(mRoot.Id, publishedMedia.Id); - Assert.AreEqual(mRoot.CreateDate.ToString("dd/MM/yyyy HH:mm:ss"), publishedMedia.CreateDate.ToString("dd/MM/yyyy HH:mm:ss")); - Assert.AreEqual(mRoot.CreatorId, publishedMedia.CreatorId); - //Assert.AreEqual(mRoot.User.Name, publishedMedia.CreatorName); - Assert.AreEqual(mRoot.ContentType.Alias, publishedMedia.ContentType.Alias); - Assert.AreEqual(mRoot.ContentType.Id, publishedMedia.ContentType.Id); - Assert.AreEqual(mRoot.Level, publishedMedia.Level); - Assert.AreEqual(mRoot.Name, publishedMedia.Name); - Assert.AreEqual(mRoot.Path, publishedMedia.Path); - Assert.AreEqual(mRoot.SortOrder, publishedMedia.SortOrder); - Assert.IsNull(publishedMedia.Parent); - } - - [TestCase("id")] - [TestCase("__NodeId")] - public void DictionaryDocument_Id_Keys(string key) - { - var dicDoc = GetDictionaryDocument(idKey: key); - DoAssert(dicDoc); - } - - [TestCase("template")] - [TestCase("templateId")] - public void DictionaryDocument_Template_Keys(string key) - { - var dicDoc = GetDictionaryDocument(templateKey: key); - DoAssert(dicDoc); - } - - [TestCase("nodeName")] - public void DictionaryDocument_NodeName_Keys(string key) - { - var dicDoc = GetDictionaryDocument(nodeNameKey: key); - DoAssert(dicDoc); - } - - [TestCase("nodeTypeAlias")] - [TestCase("__NodeTypeAlias")] - public void DictionaryDocument_NodeTypeAlias_Keys(string key) - { - var dicDoc = GetDictionaryDocument(nodeTypeAliasKey: key); - DoAssert(dicDoc); - } - - [TestCase("path")] - [TestCase("__Path")] - public void DictionaryDocument_Path_Keys(string key) - { - var dicDoc = GetDictionaryDocument(pathKey: key); - DoAssert(dicDoc); - } - - [Test] - public void DictionaryDocument_Key() - { - var key = Guid.NewGuid(); - var dicDoc = GetDictionaryDocument(keyVal: key); - DoAssert(dicDoc, keyVal: key); - } - - [Test] - public void DictionaryDocument_Get_Children() - { - var child1 = GetDictionaryDocument(idVal: 222333); - var child2 = GetDictionaryDocument(idVal: 444555); - - var dicDoc = GetDictionaryDocument(children: new List() - { - child1, child2 - }); - - Assert.AreEqual(2, dicDoc.Children.Count()); - Assert.AreEqual(222333, dicDoc.Children.ElementAt(0).Id); - Assert.AreEqual(444555, dicDoc.Children.ElementAt(1).Id); - } - - [Test] - public void Convert_From_Search_Result() - { - var ctx = GetUmbracoContext("/test"); - var key = Guid.NewGuid(); - - var fields = new Dictionary - { - {"__IndexType", "media"}, - {"__NodeId", "1234"}, - {"__NodeTypeAlias", Constants.Conventions.MediaTypes.Image}, - {"__Path", "-1,1234"}, - {"__nodeName", "Test"}, - {"id", "1234"}, - {"key", key.ToString()}, - {"urlName", "/media/test.jpg"}, - {"nodeType", "0"}, - {"sortOrder", "0"}, - {"level", "2"}, - {"nodeName", "Test"}, - {"nodeTypeAlias", Constants.Conventions.MediaTypes.Image}, - {"parentID", "-1"}, - {"path", "-1,1234"}, - {"updateDate", DateTime.Parse("2012-07-16T10:34:09").Ticks.ToString()}, - {"createDate", DateTime.Parse("2012-07-17T10:34:09").Ticks.ToString()}, - {"creatorID", _testWriterAndCreatorId.ToString()}, - {"creatorName", "Shannon"} - }; - - var result = new SearchResult("1234", 1, () => fields.ToDictionary(x => x.Key, x => new List { x.Value })); - - var store = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService(), VariationContextAccessor); - var doc = store.CreateFromCacheValues(store.ConvertFromSearchResult(result)); - - DoAssert(doc, 1234, key, null, 0, "/media/test.jpg", "Image", 23, "Shannon", "Shannon", "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2); - Assert.AreEqual(null, doc.Parent); - } - - [Test] - public void Convert_From_XPath_Navigator() - { - var ctx = GetUmbracoContext("/test"); - var key = Guid.NewGuid(); - - var xmlDoc = GetMediaXml(); - ((XmlElement)xmlDoc.DocumentElement.FirstChild).SetAttribute("key", key.ToString()); - var navigator = xmlDoc.SelectSingleNode("/root/Image").CreateNavigator(); - var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService(),VariationContextAccessor); - var doc = cache.CreateFromCacheValues(cache.ConvertFromXPathNavigator(navigator, true)); - - DoAssert(doc, 2000, key, null, 2, "image1", "Image", 23, "Shannon", "Shannon", "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1); - Assert.AreEqual(null, doc.Parent); - Assert.AreEqual(2, doc.Children.Count()); - Assert.AreEqual(2001, doc.Children.ElementAt(0).Id); - Assert.AreEqual(2002, doc.Children.ElementAt(1).Id); - } - - private XmlDocument GetMediaXml() - { - var xml = @" - - - - -]> - - - - - - - - - - -"; - xml = xml.Replace("[WriterId]", _testWriterAndCreatorId.ToString()); - xml = xml.Replace("[CreatorId]", _testWriterAndCreatorId.ToString()); - - var xmlDoc = new XmlDocument(); - xmlDoc.LoadXml(xml); - return xmlDoc; - } - - private Dictionary GetDictionary( - int id, - Guid key, - int parentId, - string idKey, - string templateKey, - string nodeNameKey, - string nodeTypeAliasKey, - string pathKey) - { - return new Dictionary() - { - {idKey, id.ToString()}, - {"key", key.ToString()}, - {templateKey, "0"}, - {"sortOrder", "44"}, - {nodeNameKey, "Testing"}, - {"urlName", "testing"}, - {nodeTypeAliasKey, "myType"}, - {"nodeType", "22"}, - {"writerID", _testWriterAndCreatorId.ToString()}, - {"creatorID", _testWriterAndCreatorId.ToString()}, - {pathKey, "1,2,3,4,5"}, - {"createDate", "2012-01-02"}, - {"updateDate", "2012-01-02"}, - {"level", "3"}, - {"parentID", parentId.ToString()} - }; - } - - private DictionaryPublishedContent GetDictionaryDocument( - string idKey = "id", - string templateKey = "template", - string nodeNameKey = "nodeName", - string nodeTypeAliasKey = "nodeTypeAlias", - string pathKey = "path", - int idVal = 1234, - Guid keyVal = default(Guid), - int parentIdVal = 321, - IEnumerable children = null) - { - if (children == null) - children = new List(); - var dicDoc = new DictionaryPublishedContent( - //the dictionary - GetDictionary(idVal, keyVal, parentIdVal, idKey, templateKey, nodeNameKey, nodeTypeAliasKey, pathKey), - //callback to get the parent - d => new DictionaryPublishedContent( - // the dictionary - GetDictionary(parentIdVal, default(Guid), -1, idKey, templateKey, nodeNameKey, nodeTypeAliasKey, pathKey), - // callback to get the parent: there is no parent - a => null, - // callback to get the children: we're not going to test this so ignore - (dd, n) => new List(), - // callback to get a property - (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a)), - null, // cache provider - VariationContextAccessor, - ContentTypesCache, - // no xpath - null, - // not from examine - false), - //callback to get the children - (dd, n) => children, - // callback to get a property - (dd, a) => dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(a)), - null, // cache provider - VariationContextAccessor, - ContentTypesCache, - // no xpath - null, - // not from examine - false); - return dicDoc; - } - - private void DoAssert( - DictionaryPublishedContent dicDoc, - int idVal = 1234, - Guid keyVal = default(Guid), - int? templateIdVal = null, - int sortOrderVal = 44, - string urlNameVal = "testing", - string nodeTypeAliasVal = "myType", - int nodeTypeIdVal = 22, - string writerNameVal = "Shannon", - string creatorNameVal = "Shannon", - string pathVal = "1,2,3,4,5", - DateTime? createDateVal = null, - DateTime? updateDateVal = null, - int levelVal = 3, - int parentIdVal = 321) - { - if (!createDateVal.HasValue) - createDateVal = DateTime.Parse("2012-01-02"); - if (!updateDateVal.HasValue) - updateDateVal = DateTime.Parse("2012-01-02"); - - DoAssert((IPublishedContent)dicDoc, idVal, keyVal, templateIdVal, sortOrderVal, urlNameVal, nodeTypeAliasVal, nodeTypeIdVal, writerNameVal, - creatorNameVal, pathVal, createDateVal, updateDateVal, levelVal); - - //now validate the parentId that has been parsed, this doesn't exist on the IPublishedContent - Assert.AreEqual(parentIdVal, dicDoc.ParentId); - } - - private void DoAssert( - IPublishedContent doc, - int idVal = 1234, - Guid keyVal = default(Guid), - int? templateIdVal = null, - int sortOrderVal = 44, - string urlNameVal = "testing", - string nodeTypeAliasVal = "myType", - int nodeTypeIdVal = 22, - string writerNameVal = "Shannon", - string creatorNameVal = "Shannon", - string pathVal = "1,2,3,4,5", - DateTime? createDateVal = null, - DateTime? updateDateVal = null, - int levelVal = 3) - { - if (!createDateVal.HasValue) - createDateVal = DateTime.Parse("2012-01-02"); - if (!updateDateVal.HasValue) - updateDateVal = DateTime.Parse("2012-01-02"); - - Assert.AreEqual(idVal, doc.Id); - Assert.AreEqual(keyVal, doc.Key); - Assert.AreEqual(templateIdVal, doc.TemplateId); - Assert.AreEqual(sortOrderVal, doc.SortOrder); - Assert.AreEqual(urlNameVal, doc.UrlSegment); - Assert.AreEqual(nodeTypeAliasVal, doc.ContentType.Alias); - Assert.AreEqual(nodeTypeIdVal, doc.ContentType.Id); - Assert.AreEqual(writerNameVal, doc.GetWriterName(ServiceContext.UserService)); - Assert.AreEqual(creatorNameVal, doc.GetCreatorName(ServiceContext.UserService)); - Assert.AreEqual(_testWriterAndCreatorId, doc.WriterId); - Assert.AreEqual(_testWriterAndCreatorId, doc.CreatorId); - Assert.AreEqual(pathVal, doc.Path); - Assert.AreEqual(createDateVal.Value, doc.CreateDate); - Assert.AreEqual(updateDateVal.Value, doc.UpdateDate); - Assert.AreEqual(levelVal, doc.Level); - } - } -} diff --git a/tests/Umbraco.Tests/Issues/U9560.cs b/tests/Umbraco.Tests/Issues/U9560.cs deleted file mode 100644 index 8687e6e07cf8..000000000000 --- a/tests/Umbraco.Tests/Issues/U9560.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using NUnit.Framework; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Issues -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, WithApplication = true)] - public class U9560 : TestWithDatabaseBase - { - [Test] - public void Test() - { - // create a content type and some properties - var contentType = new ContentType(ShortStringHelper, -1); - contentType.Alias = "test"; - contentType.Name = "test"; - var propertyType = new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext, "prop") { Name = "Prop", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }; - contentType.PropertyTypeCollection.Add(propertyType); - ServiceContext.ContentTypeService.Save(contentType); - - var aliasName = string.Empty; - - // read fields, same as what we do with PetaPoco Fetch - using (var db = Factory.GetRequiredService().CreateDatabase()) - { - db.OpenSharedConnection(); - try - { - var conn = db.Connection; - var cmd = conn.CreateCommand(); - cmd.CommandText = "SELECT mandatory, dataTypeId, propertyTypeGroupId, contentTypeId, sortOrder, alias, name, validationRegExp, description from cmsPropertyType where id=" + propertyType.Id; - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - for (var i = 0; i < reader.FieldCount; i++) - Console.WriteLine(reader.GetName(i)); - aliasName = reader.GetName(5); - } - } - } - finally - { - db.CloseSharedConnection(); - } - } - - // note that although the query is for 'alias' the field is named 'Alias' - Assert.AreEqual("Alias", aliasName); - - // try differently - using (var db = Factory.GetRequiredService().CreateDatabase()) - { - db.OpenSharedConnection(); - try - { - var conn = db.Connection; - var cmd = conn.CreateCommand(); - cmd.CommandText = "SELECT mandatory, dataTypeId, propertyTypeGroupId, contentTypeId, sortOrder, alias as alias, name, validationRegExp, description from cmsPropertyType where id=" + propertyType.Id; - using (var reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - for (var i = 0; i < reader.FieldCount; i++) - Console.WriteLine(reader.GetName(i)); - aliasName = reader.GetName(5); - } - } - } - finally - { - db.CloseSharedConnection(); - } - } - - // and now it is OK - Assert.AreEqual("alias", aliasName); - - //// get the legacy content type - //var legacyContentType = new umbraco.cms.businesslogic.ContentType(contentType.Id); - //Assert.AreEqual("test", legacyContentType.Alias); - - //// get the legacy properties - //var legacyProperties = legacyContentType.PropertyTypes; - - //// without the fix, due to some (swallowed) inner exception, we have no properties - ////Assert.IsNull(legacyProperties); - - //// thanks to the fix, it works - //Assert.IsNotNull(legacyProperties); - //Assert.AreEqual(1, legacyProperties.Count); - //Assert.AreEqual("prop", legacyProperties[0].Alias); - } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/ContentXmlDto.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/ContentXmlDto.cs deleted file mode 100644 index d7d82bdd6d33..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/ContentXmlDto.cs +++ /dev/null @@ -1,24 +0,0 @@ -using NPoco; -using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - [TableName("cmsContentXml")] - [PrimaryKey("nodeId", AutoIncrement = false)] - [ExplicitColumns] - internal class ContentXmlDto - { - [Column("nodeId")] - [PrimaryKeyColumn(AutoIncrement = false)] - [ForeignKey(typeof(ContentDto), Column = "nodeId")] - public int NodeId { get; set; } - - [Column("xml")] - [SpecialDbType(SpecialDbTypes.NTEXT)] - public string Xml { get; set; } - - [Column("rv")] - public long Rv { get; set; } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs deleted file mode 100644 index b6f886574815..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/DictionaryPublishedContent.cs +++ /dev/null @@ -1,221 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Xml.XPath; -using Examine; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Extensions; -using Umbraco.Web.Composing; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - /// - /// An IPublishedContent that is represented all by a dictionary. - /// - /// - /// This is a helper class and definitely not intended for public use, it expects that all of the values required - /// to create an IPublishedContent exist in the dictionary by specific aliases. - /// - internal class DictionaryPublishedContent : PublishedContentBase - { - // note: I'm not sure this class fully complies with IPublishedContent rules especially - // I'm not sure that _properties contains all properties including those without a value, - // neither that GetProperty will return a property without a value vs. null... @zpqrtbnk - - // List of properties that will appear in the XML and do not match - // anything in the ContentType, so they must be ignored. - private static readonly string[] IgnoredKeys = { "version", "isDoc" }; - - public DictionaryPublishedContent( - IReadOnlyDictionary valueDictionary, - Func getParent, - Func> getChildren, - Func getProperty, - IAppCache appCache, - IVariationContextAccessor variationContextAccessor, - PublishedContentTypeCache contentTypeCache, - XPathNavigator nav, - bool fromExamine):base(variationContextAccessor) - { - if (valueDictionary == null) throw new ArgumentNullException(nameof(valueDictionary)); - if (getParent == null) throw new ArgumentNullException(nameof(getParent)); - if (getProperty == null) throw new ArgumentNullException(nameof(getProperty)); - - _getParent = new Lazy(() => getParent(ParentId)); - _getChildren = new Lazy>(() => getChildren(Id, nav)); - _getProperty = getProperty; - _appCache = appCache; - - LoadedFromExamine = fromExamine; - - ValidateAndSetProperty(valueDictionary, val => _id = Int32.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int! - ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key", "__key", "__Key"); - //ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId"); - ValidateAndSetProperty(valueDictionary, val => _sortOrder = Int32.Parse(val), "sortOrder"); - ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName"); - ValidateAndSetProperty(valueDictionary, val => _urlName = val, "urlName"); - ValidateAndSetProperty(valueDictionary, val => _documentTypeAlias = val, "nodeTypeAlias", ExamineFieldNames.ItemTypeFieldName); - ValidateAndSetProperty(valueDictionary, val => _documentTypeId = Int32.Parse(val), "nodeType"); - //ValidateAndSetProperty(valueDictionary, val => _writerId = int.Parse(val), "writerID"); - ValidateAndSetProperty(valueDictionary, val => _creatorId = Int32.Parse(val), "creatorID", "writerID"); //this is a bit of a hack fix for: U4-1132 - ValidateAndSetProperty(valueDictionary, val => _path = val, "path", "__Path"); - ValidateAndSetProperty(valueDictionary, val => _createDate = ParseDateTimeValue(val), "createDate"); - ValidateAndSetProperty(valueDictionary, val => _updateDate = ParseDateTimeValue(val), "updateDate"); - ValidateAndSetProperty(valueDictionary, val => _level = Int32.Parse(val), "level"); - ValidateAndSetProperty(valueDictionary, val => - { - int pId; - ParentId = -1; - if (Int32.TryParse(val, out pId)) - { - ParentId = pId; - } - }, "parentID"); - - _contentType = contentTypeCache.Get(PublishedItemType.Media, _documentTypeAlias); - _properties = new Collection(); - - //handle content type properties - //make sure we create them even if there's no value - foreach (var propertyType in _contentType.PropertyTypes) - { - var alias = propertyType.Alias; - _keysAdded.Add(alias); - string value; - const bool isPreviewing = false; // false :: never preview a media - var property = valueDictionary.TryGetValue(alias, out value) == false || value == null - ? new XmlPublishedProperty(propertyType, this, isPreviewing) - : new XmlPublishedProperty(propertyType, this, isPreviewing, value); - _properties.Add(property); - } - - //loop through remaining values that haven't been applied - foreach (var i in valueDictionary.Where(x => - _keysAdded.Contains(x.Key) == false // not already processed - && IgnoredKeys.Contains(x.Key) == false)) // not ignorable - { - if (i.Key.InvariantStartsWith("__")) - { - // no type for that one, dunno how to convert, drop it - //IPublishedProperty property = new PropertyResult(i.Key, i.Value, PropertyResultType.CustomProperty); - //_properties.Add(property); - } - else - { - // this is a property that does not correspond to anything, ignore and log - Current.Logger.LogWarning("Dropping property '{PropertyKey}' because it does not belong to the content type.", i.Key); - } - } - } - - private DateTime ParseDateTimeValue(string val) - { - if (LoadedFromExamine == false) - return DateTime.Parse(val); - - //we need to parse the date time using Lucene converters - var ticks = Int64.Parse(val); - return new DateTime(ticks); - } - - /// - /// Flag to get/set if this was loaded from examine cache - /// - internal bool LoadedFromExamine { get; } - - //private readonly Func _getParent; - private readonly Lazy _getParent; - //private readonly Func> _getChildren; - private readonly Lazy> _getChildren; - private readonly Func _getProperty; - private readonly IAppCache _appCache; - - /// - /// Returns 'Media' as the item type - /// - public override PublishedItemType ItemType => PublishedItemType.Media; - - public override IPublishedContent Parent => _getParent.Value; - - public int ParentId { get; private set; } - - public override int Id => _id; - - public override Guid Key => _key; - - public override int? TemplateId => null; - - public override int SortOrder => _sortOrder; - - public override string Name => _name; - - private static readonly Lazy> NoCultures = new Lazy>(() => new Dictionary()); - public override IReadOnlyDictionary Cultures => NoCultures.Value; - - public override string UrlSegment => _urlName; - - public override int WriterId => _creatorId; - - public override int CreatorId => _creatorId; - - public override string Path => _path; - - public override DateTime CreateDate => _createDate; - - public override DateTime UpdateDate => _updateDate; - - public override int Level => _level; - - public override bool IsDraft(string culture = null) => false; - - public override bool IsPublished(string culture = null) => true; - - public override IEnumerable Properties => _properties; - - public override IEnumerable Children => _getChildren.Value; - - public override IEnumerable ChildrenForAllCultures => Children; - - public override IPublishedProperty GetProperty(string alias) - { - return _getProperty(this, alias); - } - - public override IPublishedContentType ContentType => _contentType; - - private readonly List _keysAdded = new List(); - private int _id; - private Guid _key; - //private int _templateId; - private int _sortOrder; - private string _name; - private string _urlName; - private string _documentTypeAlias; - private int _documentTypeId; - //private int _writerId; - private int _creatorId; - private string _path; - private DateTime _createDate; - private DateTime _updateDate; - //private Guid _version; - private int _level; - private readonly ICollection _properties; - private readonly IPublishedContentType _contentType; - - private void ValidateAndSetProperty(IReadOnlyDictionary valueDictionary, Action setProperty, params string[] potentialKeys) - { - var key = potentialKeys.FirstOrDefault(x => valueDictionary.ContainsKey(x) && valueDictionary[x] != null); - if (key == null) - { - throw new FormatException("The valueDictionary is not formatted correctly and is missing any of the '" + String.Join(",", potentialKeys) + "' elements"); - } - - setProperty(valueDictionary[key]); - _keysAdded.Add(key); - } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs deleted file mode 100644 index 0ff61e7e45d4..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/DomainCache.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Services; -using Umbraco.Extensions; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - internal class DomainCache : IDomainCache - { - private readonly IDomainService _domainService; - - public DomainCache(IDomainService domainService, IDefaultCultureAccessor defaultCultureAccessor) - { - _domainService = domainService; - DefaultCulture = defaultCultureAccessor.DefaultCulture; - } - - /// - public IEnumerable GetAll(bool includeWildcards) => _domainService.GetAll(includeWildcards) - .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, x.LanguageIsoCode, x.IsWildcard)); - - /// - public IEnumerable GetAssigned(int documentId, bool includeWildcards = false) => _domainService.GetAssignedDomains(documentId, includeWildcards) - .Where(x => x.RootContentId.HasValue && x.LanguageIsoCode.IsNullOrWhiteSpace() == false) - .Select(x => new Domain(x.Id, x.DomainName, x.RootContentId.Value, x.LanguageIsoCode, x.IsWildcard)); - - /// - public bool HasAssigned(int documentId, bool includeWildcards = false) - => documentId > 0 && GetAssigned(documentId, includeWildcards).Any(); - - /// - public string DefaultCulture { get; } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/BackgroundTaskRunner.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/BackgroundTaskRunner.cs deleted file mode 100644 index 98aa1cf8d8fe..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/BackgroundTaskRunner.cs +++ /dev/null @@ -1,863 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using System.Threading.Tasks.Dataflow; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Runtime; - -namespace Umbraco.Web.Scheduling -{ - /// - /// Manages a queue of tasks and runs them in the background. - /// - /// This class exists for logging purposes - the one you want to use is BackgroundTaskRunner{T}. - public abstract class BackgroundTaskRunner - { - /// - /// Represents a MainDom hook. - /// - public class MainDomHook - { - /// - /// Initializes a new instance of the class. - /// - /// The object. - /// A method to execute when hooking into the main domain. - /// A method to execute when the main domain releases. - public MainDomHook(IMainDom mainDom, Action install, Action release) - { - MainDom = mainDom; - Install = install; - Release = release; - } - - /// - /// Gets the object. - /// - public IMainDom MainDom { get; } - - /// - /// Gets the method to execute when hooking into the main domain. - /// - public Action Install { get; } - - /// - /// Gets the method to execute when the main domain releases. - /// - public Action Release { get; } - - internal bool Register() - { - if (MainDom != null) - { - return MainDom.Register(Install, Release); - } - - // tests - Install?.Invoke(); - return true; - } - } - } - - /// - /// Manages a queue of tasks of type and runs them in the background. - /// - /// The type of the managed tasks. - /// The task runner is web-aware and will ensure that it shuts down correctly when the AppDomain - /// shuts down (ie is unloaded). - public class BackgroundTaskRunner : BackgroundTaskRunner, IBackgroundTaskRunner - where T : class, IBackgroundTask - { - // do not remove this comment! - // - // if you plan to do anything on this class, first go and read - // http://blog.stephencleary.com/2012/12/dont-block-in-asynchronous-code.html - // http://stackoverflow.com/questions/19481964/calling-taskcompletionsource-setresult-in-a-non-blocking-manner - // http://stackoverflow.com/questions/21225361/is-there-anything-like-asynchronous-blockingcollectiont - // and more, and more, and more - // and remember: async is hard - - private readonly string _logPrefix; - private readonly BackgroundTaskRunnerOptions _options; - private readonly ILogger> _logger; - private readonly IApplicationShutdownRegistry _applicationShutdownRegistry; - private readonly object _locker = new object(); - - private readonly BufferBlock _tasks = new BufferBlock(new DataflowBlockOptions()); - - // in various places we are testing these vars outside a lock, so make them volatile - private volatile bool _isRunning; // is running - private volatile bool _completed; // does not accept tasks anymore, may still be running - - private Task _runningTask; // the threading task that is currently executing background tasks - private CancellationTokenSource _shutdownTokenSource; // used to cancel everything and shutdown - private CancellationTokenSource _cancelTokenSource; // used to cancel the current task - private CancellationToken _shutdownToken; - - private bool _terminating; // ensures we raise that event only once - private bool _terminated; // remember we've terminated - private readonly TaskCompletionSource _terminatedSource = new TaskCompletionSource(); // enable awaiting termination - - /// - /// Initializes a new instance of the class. - /// - /// A logger. - /// The application shutdown registry - /// An optional main domain hook. - public BackgroundTaskRunner(ILogger> logger, IApplicationShutdownRegistry applicationShutdownRegistry, MainDomHook hook = null) - : this(typeof(T).FullName, new BackgroundTaskRunnerOptions(), logger, applicationShutdownRegistry, hook) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The name of the runner. - /// A logger. - /// The application shutdown registry - /// An optional main domain hook. - public BackgroundTaskRunner(string name, ILogger> logger, IApplicationShutdownRegistry applicationShutdownRegistry, MainDomHook hook = null) - : this(name, new BackgroundTaskRunnerOptions(), logger, applicationShutdownRegistry, hook) - { } - - /// - /// Initializes a new instance of the class with a set of options. - /// - /// The set of options. - /// A logger. - /// The application shutdown registry - /// An optional main domain hook. - public BackgroundTaskRunner(BackgroundTaskRunnerOptions options, ILogger> logger, IApplicationShutdownRegistry applicationShutdownRegistry, MainDomHook hook = null) - : this(typeof(T).FullName, options, logger, applicationShutdownRegistry, hook) - { } - - /// - /// Initializes a new instance of the class with a set of options. - /// - /// The name of the runner. - /// The set of options. - /// A logger. - /// The application shutdown registry - /// An optional main domain hook. - public BackgroundTaskRunner(string name, BackgroundTaskRunnerOptions options, ILogger> logger, IApplicationShutdownRegistry applicationShutdownRegistry, MainDomHook hook = null) - { - _options = options ?? throw new ArgumentNullException(nameof(options)); - _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _applicationShutdownRegistry = applicationShutdownRegistry; - _logPrefix = "[" + name + "] "; - - if (options.Hosted) - _applicationShutdownRegistry.RegisterObject(this); - - if (hook != null) - _completed = _terminated = hook.Register() == false; - - if (options.AutoStart && _terminated == false) - StartUp(); - } - - /// - /// Gets the number of tasks in the queue. - /// - public int TaskCount => _tasks.Count; - - /// - /// Gets a value indicating whether a threading task is currently running. - /// - public bool IsRunning => _isRunning; - - /// - /// Gets a value indicating whether the runner has completed and cannot accept tasks anymore. - /// - public bool IsCompleted => _completed; - - /// - /// Gets the running threading task as an immutable awaitable. - /// - /// There is no running task. - /// - /// Unless the AutoStart option is true, there will be no current threading task until - /// a background task is added to the queue, and there will be no current threading task - /// when the queue is empty. In which case this method returns null. - /// The returned value can be awaited and that is all (eg no continuation). - /// - internal ThreadingTaskImmutable CurrentThreadingTask - { - get - { - lock (_locker) - { - return _runningTask == null ? null : new ThreadingTaskImmutable(_runningTask); - } - } - } - - /// - /// Gets an awaitable used to await the runner running operation. - /// - /// An awaitable instance. - /// Used to wait until the runner is no longer running (IsRunning == false), - /// though the runner could be started again afterwards by adding tasks to it. If - /// the runner is not running, returns a completed awaitable. - public ThreadingTaskImmutable StoppedAwaitable - { - get - { - lock (_locker) - { - var task = _runningTask ?? Task.CompletedTask; - return new ThreadingTaskImmutable(task); - } - } - } - - /// - /// Gets an awaitable object that can be used to await for the runner to terminate. - /// - /// An awaitable object. - /// - /// Used to wait until the runner has terminated. - /// - /// The only time the runner will be terminated is by the Hosting Environment when the application is being shutdown. - /// - /// - internal ThreadingTaskImmutable TerminatedAwaitable - { - get - { - lock (_locker) - { - return new ThreadingTaskImmutable(_terminatedSource.Task); - } - } - } - - /// - /// Adds a task to the queue. - /// - /// The task to add. - /// The task runner has completed. - public void Add(T task) - { - lock (_locker) - { - if (_completed) - throw new InvalidOperationException("The task runner has completed."); - - // add task - _logger.LogDebug("{LogPrefix} Task Added {TaskType}", _logPrefix , task.GetType().FullName); - _tasks.Post(task); - - // start - StartUpLocked(); - } - } - - /// - /// Tries to add a task to the queue. - /// - /// The task to add. - /// true if the task could be added to the queue; otherwise false. - /// Returns false if the runner is completed. - public bool TryAdd(T task) - { - lock (_locker) - { - if (_completed) - { - _logger.LogDebug("{LogPrefix} Task cannot be added {TaskType}, the task runner has already shutdown", _logPrefix, task.GetType().FullName); - return false; - } - - // add task - _logger.LogDebug("{LogPrefix} Task added {TaskType}", _logPrefix, task.GetType().FullName); - _tasks.Post(task); - - // start - StartUpLocked(); - - return true; - } - } - - /// - /// Cancels to current task, if any. - /// - /// Has no effect if the task runs synchronously, or does not want to cancel. - public void CancelCurrentBackgroundTask() - { - lock (_locker) - { - if (_completed) - throw new InvalidOperationException("The task runner has completed."); - _cancelTokenSource?.Cancel(); - } - } - - /// - /// Starts the tasks runner, if not already running. - /// - /// Is invoked each time a task is added, to ensure it is going to be processed. - /// The task runner has completed. - internal void StartUp() - { - if (_isRunning) return; - - lock (_locker) - { - if (_completed) - throw new InvalidOperationException("The task runner has completed."); - - StartUpLocked(); - } - } - - /// - /// Starts the tasks runner, if not already running. - /// - /// Must be invoked within lock(_locker) and with _isCompleted being false. - private void StartUpLocked() - { - // double check - if (_isRunning) return; - _isRunning = true; - - // create a new token source since this is a new process - _shutdownTokenSource = new CancellationTokenSource(); - _shutdownToken = _shutdownTokenSource.Token; - using (ExecutionContext.SuppressFlow()) // Do not flow AsyncLocal to the child thread - { - _runningTask = Task.Run(async () => await Pump().ConfigureAwait(false), _shutdownToken); - } - - _logger.LogDebug("{LogPrefix} Starting", _logPrefix); - } - - /// - /// Shuts the tasks runner down. - /// - /// True for force the runner to stop. - /// True to wait until the runner has stopped. - /// If is false, no more tasks can be queued but all queued tasks - /// will run. If it is true, then only the current one (if any) will end and no other task will run. - public void Shutdown(bool force, bool wait) - { - lock (_locker) - { - _completed = true; // do not accept new tasks - if (_isRunning == false) return; // done already - } - - var hasTasks = TaskCount > 0; - - if (!force && hasTasks) - { - _logger.LogInformation("{LogPrefix} Waiting for tasks to complete", _logPrefix); - } - - // complete the queue - // will stop waiting on the queue or on a latch - _tasks.Complete(); - - if (force) - { - // we must bring everything down, now - lock (_locker) - { - // was Complete() enough? - // if _tasks.Complete() ended up triggering code to stop the runner and reset - // the _isRunning flag, then there's no need to initiate a cancel on the cancelation token. - if (_isRunning == false) - return; - } - - // try to cancel running async tasks (cannot do much about sync tasks) - // break latched tasks - // stop processing the queue - _shutdownTokenSource?.Cancel(false); // false is the default - _shutdownTokenSource?.Dispose(); - _shutdownTokenSource = null; - } - - // tasks in the queue will be executed... - if (!wait) return; - - _runningTask?.Wait(CancellationToken.None); // wait for whatever is running to end... - } - - private async Task Pump() - { - while (true) - { - // get the next task - // if it returns null the runner is going down, stop - var bgTask = await GetNextBackgroundTask(_shutdownToken); - if (bgTask == null) return; - - // set a cancellation source so that the current task can be cancelled - // link from _shutdownToken so that we can use _cancelTokenSource for both - lock (_locker) - { - _cancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(_shutdownToken); - } - - try - { - // wait for latch should return the task - // if it returns null it's either that the task has been cancelled - // or the whole runner is going down - in both cases, continue, - // and GetNextBackgroundTask will take care of shutdowns - bgTask = await WaitForLatch(bgTask, _cancelTokenSource.Token); - - if (bgTask != null) - { - // executes & be safe - RunAsync should NOT throw but only raise an event, - // but... just make sure we never ever take everything down - try - { - await RunAsync(bgTask, _cancelTokenSource.Token).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.LogError(ex, "{LogPrefix} Task runner exception", _logPrefix); - } - } - } - finally - { - // done - lock (_locker) - { - // always dispose CancellationTokenSource when you are done using them - // https://lowleveldesign.org/2015/11/30/catch-in-cancellationtokensource/ - _cancelTokenSource.Dispose(); - _cancelTokenSource = null; - } - } - } - } - - // gets the next background task from the buffer - private async Task GetNextBackgroundTask(CancellationToken token) - { - while (true) - { - var task = await GetNextBackgroundTask2(token); - if (task != null) return task; - - lock (_locker) - { - // deal with race condition - if (_shutdownToken.IsCancellationRequested == false && TaskCount > 0) continue; - - // if we really have nothing to do, stop - _logger.LogDebug("{LogPrefix} Stopping", _logPrefix); - - if (_options.PreserveRunningTask == false) - _runningTask = null; - _isRunning = false; - _shutdownToken = CancellationToken.None; - } - - OnEvent(Stopped, "Stopped"); - return null; - } - } - - private async Task GetNextBackgroundTask2(CancellationToken shutdownToken) - { - // exit if canceling - if (shutdownToken.IsCancellationRequested) - return null; - - // if KeepAlive is false then don't block, exit if there is - // no task in the buffer - yes, there is a race condition, which - // we'll take care of - if (_options.KeepAlive == false && TaskCount == 0) - return null; - - try - { - // A Task that informs of whether and when more output is available. If, when the - // task completes, its Result is true, more output is available in the source (though another - // consumer of the source may retrieve the data). If it returns false, more output is not - // and will never be available, due to the source completing prior to output being available. - - var output = await _tasks.OutputAvailableAsync(shutdownToken); // block until output or cancelled - if (output == false) return null; - } - catch (TaskCanceledException) - { - return null; - } - - try - { - // A task that represents the asynchronous receive operation. When an item value is successfully - // received from the source, the returned task is completed and its Result returns the received - // value. If an item value cannot be retrieved because the source is empty and completed, an - // InvalidOperationException exception is thrown in the returned task. - - // the source cannot be empty *and* completed here - we know we have output - return await _tasks.ReceiveAsync(shutdownToken); - } - catch (TaskCanceledException) - { - return null; - } - } - - // if bgTask is not a latched background task, or if it is not latched, returns immediately - // else waits for the latch, taking care of completion and shutdown and whatnot - private async Task WaitForLatch(T bgTask, CancellationToken token) - { - var latched = bgTask as ILatchedBackgroundTask; - if (latched == null || latched.IsLatched == false) return bgTask; - - // support canceling awaiting - // read https://github.com/dotnet/corefx/issues/2704 - // read http://stackoverflow.com/questions/27238232/how-can-i-cancel-task-whenall - var tokenTaskSource = new TaskCompletionSource(); - token.Register(s => ((TaskCompletionSource)s).SetResult(true), tokenTaskSource); - - // returns the task that completed - // - latched.Latch completes when the latch releases - // - _tasks.Completion completes when the runner completes - // - tokenTaskSource.Task completes when this task, or the whole runner is cancelled - var task = await Task.WhenAny(latched.Latch, _tasks.Completion, tokenTaskSource.Task); - - // ok to run now - if (task == latched.Latch) - return bgTask; - - // we are shutting down if the _tasks.Complete(); was called or the shutdown token was cancelled - var isShuttingDown = _shutdownToken.IsCancellationRequested || task == _tasks.Completion; - - // if shutting down, return the task only if it runs on shutdown - if (isShuttingDown && latched.RunsOnShutdown) - return bgTask; - - // else, either it does not run on shutdown or it's been cancelled, dispose - latched.Dispose(); - return null; - } - - // runs the background task, taking care of shutdown (as far as possible - cannot abort - // a non-async Run for example, so we'll do our best) - private async Task RunAsync(T bgTask, CancellationToken token) - { - try - { - OnTaskStarting(new TaskEventArgs(bgTask)); - - try - { - try - { - if (bgTask.IsAsync) - { - // configure await = false since we don't care about the context, we're on a background thread. - await bgTask.RunAsync(token).ConfigureAwait(false); - } - else - { - bgTask.Run(); - } - } - finally // ensure we disposed - unless latched again ie wants to re-run - { - if (!(bgTask is ILatchedBackgroundTask lbgTask) || lbgTask.IsLatched == false) - { - bgTask.Dispose(); - } - } - } - catch (Exception e) - { - OnTaskError(new TaskEventArgs(bgTask, e)); - throw; - } - - OnTaskCompleted(new TaskEventArgs(bgTask)); - } - catch (Exception ex) - { - - _logger.LogError(ex, "{LogPrefix} Task has failed", _logPrefix); - } - } - - #region Events - - // triggers when a background task starts - public event TypedEventHandler, TaskEventArgs> TaskStarting; - - // triggers when a background task has completed - public event TypedEventHandler, TaskEventArgs> TaskCompleted; - - // triggers when a background task throws - public event TypedEventHandler, TaskEventArgs> TaskError; - - // triggers when a background task is cancelled - public event TypedEventHandler, TaskEventArgs> TaskCancelled; - - // triggers when the runner stops (but could start again if a task is added to it) - internal event TypedEventHandler, EventArgs> Stopped; - - // triggers when the hosting environment requests that the runner terminates - internal event TypedEventHandler, EventArgs> Terminating; - - // triggers when the hosting environment has terminated (no task can be added, no task is running) - internal event TypedEventHandler, EventArgs> Terminated; - - private void OnEvent(TypedEventHandler, EventArgs> handler, string name) - { - OnEvent(handler, name, EventArgs.Empty); - } - - private void OnEvent(TypedEventHandler, TArgs> handler, string name, TArgs e) - { - _logger.LogDebug("{LogPrefix} OnEvent {EventName}", _logPrefix, name); - - if (handler == null) return; - - try - { - handler(this, e); - } - catch (Exception ex) - { - _logger.LogError(ex, "{LogPrefix} {Name} exception occurred", _logPrefix, name); - } - } - - protected virtual void OnTaskError(TaskEventArgs e) - { - OnEvent(TaskError, "TaskError", e); - } - - protected virtual void OnTaskStarting(TaskEventArgs e) - { - OnEvent(TaskStarting, "TaskStarting", e); - } - - protected virtual void OnTaskCompleted(TaskEventArgs e) - { - OnEvent(TaskCompleted, "TaskCompleted", e); - } - - protected virtual void OnTaskCancelled(TaskEventArgs e) - { - OnEvent(TaskCancelled, "TaskCancelled", e); - - // dispose it - e.Task.Dispose(); - } - - #endregion - - #region IDisposable - - private readonly object _disposalLocker = new object(); - public bool IsDisposed { get; private set; } - - ~BackgroundTaskRunner() - { - Dispose(false); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (IsDisposed || disposing == false) - return; - - lock (_disposalLocker) - { - if (IsDisposed) - return; - DisposeResources(); - IsDisposed = true; - } - } - - protected virtual void DisposeResources() - { - // just make sure we eventually go down - Shutdown(true, false); - } - - #endregion - - #region IRegisteredObject.Stop - - /// - /// Used by IRegisteredObject.Stop and shutdown on threadpool threads to not block shutdown times. - /// - /// - /// - /// An awaitable Task that is used to handle the shutdown. - /// - internal Task StopInternal(bool immediate) - { - // the first time the hosting environment requests that the runner terminates, - // raise the Terminating event - that could be used to prevent any process that - // would expect the runner to be available from starting. - var onTerminating = false; - lock (_locker) - { - if (_terminating == false) - { - _terminating = true; - _logger.LogInformation("{LogPrefix} Terminating {Immediate}", _logPrefix, immediate ? immediate.ToString() : string.Empty); - onTerminating = true; - } - } - - if (onTerminating) - OnEvent(Terminating, "Terminating"); - - // Run the Stop commands on another thread since IRegisteredObject.Stop calls are called sequentially - // with a single aspnet thread during shutdown and we don't want to delay other calls to IRegisteredObject.Stop. - if (!immediate) - { - using (ExecutionContext.SuppressFlow()) - { - return Task.Run(StopInitial, CancellationToken.None); - } - } - else - { - lock (_locker) - { - if (_terminated) return Task.CompletedTask; - using (ExecutionContext.SuppressFlow()) - { - return Task.Run(StopImmediate, CancellationToken.None); - } - } - } - } - - /// - /// Requests a registered object to un-register. - /// - /// true to indicate the registered object should un-register from the hosting - /// environment before returning; otherwise, false. - /// - /// "When the application manager needs to stop a registered object, it will call the Stop method." - /// The application manager will call the Stop method to ask a registered object to un-register. During - /// processing of the Stop method, the registered object must call the applicationShutdownRegistry.UnregisterObject method. - /// - public void Stop(bool immediate) => StopInternal(immediate); - - /// - /// Called when immediate == false for IRegisteredObject.Stop(bool immediate) - /// - /// - /// Called on a threadpool thread - /// - private void StopInitial() - { - // immediate == false when the app is trying to wind down, immediate == true will be called either: - // after a call with immediate == false or if the app is not trying to wind down and needs to immediately stop. - // So Stop may be called twice or sometimes only once. - - try - { - Shutdown(false, false); // do not accept any more tasks, flush the queue, do not wait - } - finally - { - // raise the completed event only after the running threading task has completed - lock (_locker) - { - if (_runningTask != null) - { - _runningTask.ContinueWith( - _ => StopImmediate(), - // Must explicitly specify this, see https://blog.stephencleary.com/2013/10/continuewith-is-dangerous-too.html - TaskScheduler.Default); - } - else - { - StopImmediate(); - } - - } - } - - // If the shutdown token was not canceled in the Shutdown call above, it means there was still tasks - // being processed, in which case we'll give it a couple seconds - if (!_shutdownToken.IsCancellationRequested) - { - // If we are called with immediate == false, wind down above and then shutdown within 2 seconds, - // we want to shut down the app as quick as possible, if we wait until immediate == true, this can - // take a very long time since immediate will only be true when a new request is received on the new - // appdomain (or another iis timeout occurs ... which can take some time). - Thread.Sleep(2000); //we are already on a threadpool thread - StopImmediate(); - } - } - - /// - /// Called when immediate == true for IRegisteredObject.Stop(bool immediate) - /// - /// - /// Called on a threadpool thread - /// - private void StopImmediate() - { - _logger.LogInformation("{LogPrefix} Canceling tasks", _logPrefix); - try - { - Shutdown(true, true); // cancel all tasks, wait for the current one to end - } - finally - { - Terminate(true); - } - } - - // called by Stop either immediately or eventually - private void Terminate(bool immediate) - { - // signal the environment we have terminated - // log - // raise the Terminated event - // complete the awaitable completion source, if any - - if (immediate) - { - //only unregister when it's the final call, else we won't be notified of the final call - _applicationShutdownRegistry.UnregisterObject(this); - } - - if (_terminated) return; // already taken care of - - TaskCompletionSource terminatedSource; - lock (_locker) - { - _terminated = true; - terminatedSource = _terminatedSource; - } - - _logger.LogInformation("{LogPrefix} Tasks {TaskStatus}, terminated", - _logPrefix, - immediate ? "cancelled" : "completed"); - - OnEvent(Terminated, "Terminated"); - - terminatedSource.TrySetResult(0); - } - - #endregion - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/BackgroundTaskRunnerOptions.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/BackgroundTaskRunnerOptions.cs deleted file mode 100644 index bc0369fee83a..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/BackgroundTaskRunnerOptions.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace Umbraco.Web.Scheduling -{ - /// - /// Provides options to the class. - /// - public class BackgroundTaskRunnerOptions - { - // TODO: Could add options for using a stack vs queue if required - - /// - /// Initializes a new instance of the class. - /// - public BackgroundTaskRunnerOptions() - { - LongRunning = false; - KeepAlive = false; - AutoStart = false; - PreserveRunningTask = false; - Hosted = true; - } - - /// - /// Gets or sets a value indicating whether the running task should be a long-running, - /// coarse grained operation. - /// - public bool LongRunning { get; set; } - - /// - /// Gets or sets a value indicating whether the running task should block and wait - /// on the queue, or end, when the queue is empty. - /// - public bool KeepAlive { get; set; } - - /// - /// Gets or sets a value indicating whether the running task should start immediately - /// or only once a task has been added to the queue. - /// - public bool AutoStart { get; set; } - - /// - /// Gets or sets a value indicating whether the running task should be preserved - /// once completed, or reset to null. For unit tests. - /// - public bool PreserveRunningTask { get; set; } - - /// - /// Gets or sets a value indicating whether the runner should register with (and be - /// stopped by) the hosting. Otherwise, something else should take care of stopping - /// the runner. True by default. - /// - public bool Hosted { get; set; } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/IBackgroundTask.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/IBackgroundTask.cs deleted file mode 100644 index b1285d0080e2..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/IBackgroundTask.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Umbraco.Web.Scheduling -{ - /// - /// Represents a background task. - /// - public interface IBackgroundTask : IDisposable - { - /// - /// Runs the background task. - /// - void Run(); - - /// - /// Runs the task asynchronously. - /// - /// A cancellation token. - /// A instance representing the execution of the background task. - /// The background task cannot run asynchronously. - Task RunAsync(CancellationToken token); - - /// - /// Indicates whether the background task can run asynchronously. - /// - bool IsAsync { get; } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/IBackgroundTaskRunner.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/IBackgroundTaskRunner.cs deleted file mode 100644 index 52dc75f3fbdb..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/IBackgroundTaskRunner.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using Umbraco.Cms.Core; - -namespace Umbraco.Web.Scheduling -{ - /// - /// Defines a service managing a queue of tasks of type and running them in the background. - /// - /// The type of the managed tasks. - /// The interface is not complete and exists only to have the contravariance on T. - public interface IBackgroundTaskRunner : IDisposable, IRegisteredObject - where T : class, IBackgroundTask - { - bool IsCompleted { get; } - void Add(T task); - bool TryAdd(T task); - - // TODO: complete the interface? - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/ILatchedBackgroundTask.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/ILatchedBackgroundTask.cs deleted file mode 100644 index e981623b34c1..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/ILatchedBackgroundTask.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Threading.Tasks; - -namespace Umbraco.Web.Scheduling -{ - /// - /// Represents a latched background task. - /// - /// Latched background tasks can suspend their execution until - /// a condition is met. However if the tasks runner has to terminate, - /// latched background tasks can be executed immediately, depending on - /// the value returned by RunsOnShutdown. - public interface ILatchedBackgroundTask : IBackgroundTask - { - /// - /// Gets a task on latch. - /// - /// The task is not latched. - Task Latch { get; } - - /// - /// Gets a value indicating whether the task is latched. - /// - /// Should return false as soon as the condition is met. - bool IsLatched { get; } - - /// - /// Gets a value indicating whether the task can be executed immediately if the task runner has to terminate. - /// - bool RunsOnShutdown { get; } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/LatchedBackgroundTaskBase.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/LatchedBackgroundTaskBase.cs deleted file mode 100644 index 738bed9b5bdf..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/LatchedBackgroundTaskBase.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Umbraco.Cms.Core; - -namespace Umbraco.Web.Scheduling -{ - public abstract class LatchedBackgroundTaskBase : DisposableObjectSlim, ILatchedBackgroundTask - { - private TaskCompletionSource _latch; - - protected LatchedBackgroundTaskBase() - { - _latch = new TaskCompletionSource(); - } - - /// - /// Implements IBackgroundTask.Run(). - /// - public virtual void Run() - { - throw new NotSupportedException("This task cannot run synchronously."); - } - - /// - /// Implements IBackgroundTask.RunAsync(). - /// - public virtual Task RunAsync(CancellationToken token) - { - throw new NotSupportedException("This task cannot run asynchronously."); - } - - /// - /// Indicates whether the background task can run asynchronously. - /// - public abstract bool IsAsync { get; } - - public Task Latch => _latch.Task; - - public bool IsLatched => _latch.Task.IsCompleted == false; - - protected void Release() - { - _latch.SetResult(true); - } - - protected void Reset() - { - _latch = new TaskCompletionSource(); - } - - public virtual bool RunsOnShutdown => false; - - // the task is going to be disposed after execution, - // unless it is latched again, thus indicating it wants to - // remain active - - protected override void DisposeResources() - { } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/TaskEventArgs.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/TaskEventArgs.cs deleted file mode 100644 index 972bc1290151..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/TaskEventArgs.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; - -namespace Umbraco.Web.Scheduling -{ - /// - /// Provides arguments for task runner events. - /// - /// The type of the task. - public class TaskEventArgs : EventArgs - where T : IBackgroundTask - { - /// - /// Initializes a new instance of the class with a task. - /// - /// The task. - public TaskEventArgs(T task) - { - Task = task; - } - - /// - /// Initializes a new instance of the class with a task and an exception. - /// - /// The task. - /// An exception. - public TaskEventArgs(T task, Exception exception) - { - Task = task; - Exception = exception; - } - - /// - /// Gets or sets the task. - /// - public T Task { get; private set; } - - /// - /// Gets or sets the exception. - /// - public Exception Exception { get; private set; } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/ThreadingTaskImmutable.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/ThreadingTaskImmutable.cs deleted file mode 100644 index b1ea0d7f71ba..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/LegacyBackgroundTask/ThreadingTaskImmutable.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; - -namespace Umbraco.Web.Scheduling -{ - /// - /// Wraps a within an object that gives access to its GetAwaiter method and Status - /// property while ensuring that it cannot be modified in any way. - /// - public class ThreadingTaskImmutable - { - private readonly Task _task; - - /// - /// Initializes a new instance of the class with a Task. - /// - /// The task. - public ThreadingTaskImmutable(Task task) - { - if (task == null) - throw new ArgumentNullException("task"); - _task = task; - } - - /// - /// Gets an awaiter used to await the task. - /// - /// An awaiter instance. - public TaskAwaiter GetAwaiter() - { - return _task.GetAwaiter(); - } - - /// - /// Gets the TaskStatus of the task. - /// - /// The current TaskStatus of the task. - public TaskStatus Status - { - get { return _task.Status; } - } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PreviewContent.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/PreviewContent.cs deleted file mode 100644 index f190b7ee41f7..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PreviewContent.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Xml; -using Microsoft.Extensions.Logging; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.Composing; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - class PreviewContent - { - private readonly int _userId; - private readonly Guid _previewSet; - private string _previewSetPath; - private XmlDocument _previewXml; - private readonly XmlStore _xmlStore; - - /// - /// Gets the XML document. - /// - /// May return null if the preview content set is invalid. - public XmlDocument XmlContent - { - get - { - // null if invalid preview content - if (_previewSetPath == null) return null; - - // load if not loaded yet - if (_previewXml != null) - return _previewXml; - - _previewXml = new XmlDocument(); - - try - { - _previewXml.Load(_previewSetPath); - } - catch (Exception ex) - { - Current.Logger.LogError(ex, "Could not load preview set {PreviewSet} for user {UserId}.", _previewSet, _userId); - - ClearPreviewSet(); - - _previewXml = null; - _previewSetPath = null; // do not try again - } - - return _previewXml; - } - } - - /// - /// Gets the preview token. - /// - /// To be stored in a cookie or wherever appropriate. - public string Token => _userId + ":" + _previewSet; - - /// - /// Initializes a new instance of the class for a user. - /// - /// The underlying Xml store. - /// The user identifier. - public PreviewContent(XmlStore xmlStore, int userId) - { - if (xmlStore == null) - throw new ArgumentNullException(nameof(xmlStore)); - _xmlStore = xmlStore; - - _userId = userId; - _previewSet = Guid.NewGuid(); - _previewSetPath = GetPreviewSetPath(_userId, _previewSet); - } - - /// - /// Initializes a new instance of the with a preview token. - /// - /// The underlying Xml store. - /// The preview token. - public PreviewContent(XmlStore xmlStore, string token) - { - if (xmlStore == null) - throw new ArgumentNullException(nameof(xmlStore)); - _xmlStore = xmlStore; - - if (token.IsNullOrWhiteSpace()) - throw new ArgumentException("Null or empty token.", nameof(token)); - var parts = token.Split(':'); - if (parts.Length != 2) - throw new ArgumentException("Invalid token.", nameof(token)); - - if (int.TryParse(parts[0], out _userId) == false) - throw new ArgumentException("Invalid token.", nameof(token)); - if (Guid.TryParse(parts[1], out _previewSet) == false) - throw new ArgumentException("Invalid token.", nameof(token)); - - _previewSetPath = GetPreviewSetPath(_userId, _previewSet); - } - - // creates and saves a new preview set - // used in 2 places and each time includeSubs is true - // have to use the Document class at the moment because IContent does not do ToXml... - public void CreatePreviewSet(int contentId, bool includeSubs) - { - // note: always include subs - _previewXml = _xmlStore.GetPreviewXml(contentId, includeSubs); - - // make sure the preview folder exists - var dir = new DirectoryInfo(TestHelper.IOHelper.MapPath(Constants.SystemDirectories.Preview)); - if (dir.Exists == false) - dir.Create(); - - // clean old preview sets - ClearPreviewDirectory(_userId, dir); - - // save - _previewXml.Save(_previewSetPath); - } - - // get the full path to the preview set - private static string GetPreviewSetPath(int userId, Guid previewSet) - { - return TestHelper.IOHelper.MapPath(Path.Combine(Constants.SystemDirectories.Preview, userId + "_" + previewSet + ".config")); - } - - // deletes files for the user, and files accessed more than one hour ago - private static void ClearPreviewDirectory(int userId, DirectoryInfo dir) - { - var now = DateTime.Now; - var prefix = userId + "_"; - foreach (var file in dir.GetFiles("*.config") - .Where(x => x.Name.StartsWith(prefix) || (now - x.LastAccessTime).TotalMinutes > 1)) - { - DeletePreviewSetFile(userId, file); - } - } - - // delete one preview set file in a safe way - private static void DeletePreviewSetFile(int userId, FileSystemInfo file) - { - try - { - file.Delete(); - } - catch (Exception ex) - { - Current.Logger.LogError(ex, "Couldn't delete preview set {FileName} for user {UserId}", file.Name, userId); - } - } - - /// - /// Deletes the preview set in a safe way. - /// - public void ClearPreviewSet() - { - if (_previewSetPath == null) return; - var previewSetFile = new FileInfo(_previewSetPath); - DeletePreviewSetFile(_userId, previewSetFile); - } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PreviewXmlDto.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/PreviewXmlDto.cs deleted file mode 100644 index ba49167fda2b..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PreviewXmlDto.cs +++ /dev/null @@ -1,24 +0,0 @@ -using NPoco; -using Umbraco.Cms.Infrastructure.Persistence.DatabaseAnnotations; -using Umbraco.Cms.Infrastructure.Persistence.Dtos; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - [TableName("cmsPreviewXml")] - [PrimaryKey("nodeId", AutoIncrement = false)] - [ExplicitColumns] - internal class PreviewXmlDto - { - [Column("nodeId")] - [PrimaryKeyColumn(AutoIncrement = false)] - [ForeignKey(typeof(ContentDto), Column = "nodeId")] - public int NodeId { get; set; } - - [Column("xml")] - [SpecialDbType(SpecialDbTypes.NTEXT)] - public string Xml { get; set; } - - [Column("rv")] - public long Rv { get; set; } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs deleted file mode 100644 index 6bfaa075b2d2..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedContentCache.cs +++ /dev/null @@ -1,548 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Xml; -using System.Xml.XPath; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Xml; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - internal class PublishedContentCache : PublishedCacheBase, IPublishedContentCache - { - private readonly IAppCache _appCache; - private readonly GlobalSettings _globalSettings; - private readonly RoutesCache _routesCache; - private readonly IVariationContextAccessor _variationContextAccessor; - private readonly IDomainCache _domainCache; - private readonly PublishedContentTypeCache _contentTypeCache; - - // initialize a PublishedContentCache instance with - // an XmlStore containing the master xml - // an IAppCache that should be at request-level - // a RoutesCache - need to cleanup that one - // a preview token string (or null if not previewing) - public PublishedContentCache( - XmlStore xmlStore, // an XmlStore containing the master xml - IDomainCache domainCache, // an IDomainCache implementation - IAppCache appCache, // an IAppCache that should be at request-level - GlobalSettings globalSettings, - PublishedContentTypeCache contentTypeCache, // a PublishedContentType cache - RoutesCache routesCache, // a RoutesCache - IVariationContextAccessor variationContextAccessor, - string previewToken) // a preview token string (or null if not previewing) - : base(previewToken.IsNullOrWhiteSpace() == false) - { - _appCache = appCache; - _globalSettings = globalSettings; - _routesCache = routesCache; // may be null for unit-testing - _variationContextAccessor = variationContextAccessor; - _contentTypeCache = contentTypeCache; - _domainCache = domainCache; - - _xmlStore = xmlStore; - _xml = _xmlStore.Xml; // capture - because the cache has to remain consistent - - if (previewToken.IsNullOrWhiteSpace() == false) - _previewContent = new PreviewContent(_xmlStore, previewToken); - } - - #region Unit Tests - - // for INTERNAL, UNIT TESTS use ONLY - internal RoutesCache RoutesCache => _routesCache; - - // for INTERNAL, UNIT TESTS use ONLY - internal XmlStore XmlStore => _xmlStore; - - #endregion - - #region Routes - - public virtual IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null) - { - if (route == null) throw new ArgumentNullException(nameof(route)); - - // try to get from cache if not previewing - var contentId = preview || _routesCache == null ? 0 : _routesCache.GetNodeId(route); - - // if found id in cache then get corresponding content - // and clear cache if not found - for whatever reason - IPublishedContent content = null; - if (contentId > 0) - { - content = GetById(preview, contentId); - if (content == null) - _routesCache?.ClearNode(contentId); - } - - // still have nothing? actually determine the id - hideTopLevelNode = hideTopLevelNode ?? _globalSettings.HideTopLevelNodeFromPath; // default = settings - content = content ?? DetermineIdByRoute(preview, route, hideTopLevelNode.Value); - - // cache if we have a content and not previewing - if (content != null && preview == false && _routesCache != null) - AddToCacheIfDeepestRoute(content, route); - - return content; - } - - private void AddToCacheIfDeepestRoute(IPublishedContent content, string route) - { - var domainRootNodeId = route.StartsWith("/") ? -1 : int.Parse(route.Substring(0, route.IndexOf('/'))); - - // so we have a route that maps to a content... say "1234/path/to/content" - however, there could be a - // domain set on "to" and route "4567/content" would also map to the same content - and due to how - // URLs computing work (by walking the tree up to the first domain we find) it is that second route - // that would be returned - the "deepest" route - and that is the route we want to cache, *not* the - // longer one - so make sure we don't cache the wrong route - - var deepest = DomainUtilities.ExistsDomainInPath(_domainCache.GetAll(false), content.Path, domainRootNodeId) == false; - - if (deepest) - _routesCache.Store(content.Id, route, true); // trusted route - } - - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null) - { - return GetByRoute(PreviewDefault, route, hideTopLevelNode); - } - - public virtual string GetRouteById(bool preview, int contentId, string culture = null) - { - // try to get from cache if not previewing - var route = preview || _routesCache == null ? null : _routesCache.GetRoute(contentId); - - // if found in cache then return - if (route != null) - return route; - - // else actually determine the route - route = DetermineRouteById(preview, contentId); - - // node not found - if (route == null) - return null; - - // cache the route BUT do NOT trust it as it can be a colliding route - // meaning if we GetRouteById again, we'll get it from cache, but it - // won't be used for inbound routing - if (preview == false) - _routesCache.Store(contentId, route, false); - - return route; - } - - public string GetRouteById(int contentId, string culture = null) - { - return GetRouteById(PreviewDefault, contentId, culture); - } - - IPublishedContent DetermineIdByRoute(bool preview, string route, bool hideTopLevelNode) - { - //the route always needs to be lower case because we only store the urlName attribute in lower case - route = route?.ToLowerInvariant() ?? throw new ArgumentNullException(nameof(route)); - - var pos = route.IndexOf('/'); - var path = pos == 0 ? route : route.Substring(pos); - var startNodeId = pos == 0 ? 0 : int.Parse(route.Substring(0, pos)); - - //check if we can find the node in our xml cache - var id = NavigateRoute(preview, startNodeId, path, hideTopLevelNode); - return id > 0 ? GetById(preview, id) : null; - } - - private static XmlElement GetXmlElementChildWithLowestSortOrder(XmlNode element) - { - XmlElement elt = null; - var min = int.MaxValue; - foreach (var n in element.ChildNodes) - { - var e = n as XmlElement; - if (e == null) continue; - - var sortOrder = int.Parse(e.GetAttribute("sortOrder")); - if (sortOrder >= min) continue; - - min = sortOrder; - elt = e; - } - return elt; - } - - private int NavigateRoute(bool preview, int startNodeId, string path, bool hideTopLevelNode) - { - var xml = GetXml(preview); - XmlElement elt; - - // empty path - if (path == string.Empty || path == "/") - { - if (startNodeId > 0) - { - elt = xml.GetElementById(startNodeId.ToString(CultureInfo.InvariantCulture)); - return elt == null ? -1 : startNodeId; - } - - elt = GetXmlElementChildWithLowestSortOrder(xml.DocumentElement); - return elt == null ? -1 : int.Parse(elt.GetAttribute("id")); - } - - // non-empty path - elt = startNodeId <= 0 - ? xml.DocumentElement - : xml.GetElementById(startNodeId.ToString(CultureInfo.InvariantCulture)); - if (elt == null) return -1; - - var urlParts = path.Split(SlashChar, StringSplitOptions.RemoveEmptyEntries); - - if (hideTopLevelNode && startNodeId <= 0) - { - //Don't use OfType or Cast, this is critical code, all ChildNodes are XmlElement so explicitly cast - // https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198 - foreach (var n in elt.ChildNodes) - { - var e = n as XmlElement; - if (e == null) continue; - - var id = NavigateElementRoute(e, urlParts); - if (id > 0) return id; - } - - if (urlParts.Length > 1) - return -1; - } - - return NavigateElementRoute(elt, urlParts); - } - - private static int NavigateElementRoute(XmlElement elt, string[] urlParts) - { - var found = true; - var i = 0; - while (found && i < urlParts.Length) - { - found = false; - //Don't use OfType or Cast, this is critical code, all ChildNodes are XmlElement so explicitly cast - // https://gist.github.com/Shazwazza/04e2e5642a316f4a87e52dada2901198 - var sortOrder = -1; - foreach (var o in elt.ChildNodes) - { - var child = o as XmlElement; - if (child == null) continue; - - var noNode = child.GetAttributeNode("isDoc") == null; - if (noNode) continue; - if (child.GetAttribute("urlName") != urlParts[i]) continue; - - found = true; - - var so = int.Parse(child.GetAttribute("sortOrder")); - if (sortOrder >= 0 && so >= sortOrder) continue; - - sortOrder = so; - elt = child; - } - i++; - } - return found ? int.Parse(elt.GetAttribute("id")) : -1; - } - - string DetermineRouteById(bool preview, int contentId) - { - var node = GetById(preview, contentId); - if (node == null) return null; - - // walk up from that node until we hit a node with a domain, - // or we reach the content root, collecting URLs in the way - var pathParts = new List(); - var n = node; - var hasDomains = _domainCache.HasAssigned(n.Id); - while (hasDomains == false && n != null) // n is null at root - { - // get the url - var urlName = n.UrlSegment(TestHelper.VariationContextAccessor); - pathParts.Add(urlName); - - // move to parent node - n = n.Parent; - hasDomains = n != null && _domainCache.HasAssigned(n.Id); - } - - // no domain, respect HideTopLevelNodeFromPath for legacy purposes - if (hasDomains == false && _globalSettings.HideTopLevelNodeFromPath) - { - if (node.Parent == null) - { - var rootNode = GetByRoute(preview, "/", true); - if (rootNode == null) - throw new Exception("Failed to get node at /."); - if (rootNode.Id == node.Id) // remove only if we're the default node - pathParts.RemoveAt(pathParts.Count - 1); - } - else - { - pathParts.RemoveAt(pathParts.Count - 1); - } - } - - // assemble the route - pathParts.Reverse(); - var path = "/" + string.Join("/", pathParts); // will be "/" or "/foo" or "/foo/bar" etc - var route = (n?.Id.ToString(CultureInfo.InvariantCulture) ?? "") + path; - - return route; - } - - #endregion - - #region XPath Strings - - static class XPathStrings - { - public const string Root = "/root"; - public const string RootDocuments = "/root/* [@isDoc]"; - } - - #endregion - - #region Converters - - private IPublishedContent ConvertToDocument(XmlNode xmlNode, bool isPreviewing) - { - return xmlNode == null ? null : XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache, _variationContextAccessor); - } - - private IEnumerable ConvertToDocuments(XmlNodeList xmlNodes, bool isPreviewing) - { - return xmlNodes.Cast() - .Select(xmlNode => XmlPublishedContent.Get(xmlNode, isPreviewing, _appCache, _contentTypeCache, _variationContextAccessor)); - } - - #endregion - - #region Getters - - public override IPublishedContent GetById(bool preview, int nodeId) - { - return ConvertToDocument(GetXml(preview).GetElementById(nodeId.ToString(CultureInfo.InvariantCulture)), preview); - } - - public override IPublishedContent GetById(bool preview, Guid nodeId) - { - // implement this, but in a more efficient way - //const string xpath = "//* [@isDoc and @key=$guid]"; - //return GetSingleByXPath(preview, xpath, new[] { new XPathVariable("guid", nodeId.ToString()) }); - - var keyMatch = nodeId.ToString(); - - var nav = GetXml(preview).CreateNavigator(); - if (nav.MoveToFirstChild() == false) return null; // from / to /root - if (nav.MoveToFirstChild() == false) return null; // from /root to /root/* - - while (true) - { - var isDoc = false; - string key = null; - - if (nav.HasAttributes) - { - nav.MoveToFirstAttribute(); - do - { - if (nav.Name == "isDoc") isDoc = true; - if (nav.Name == "key") key = nav.Value; - if (isDoc && key != null) break; - } while (nav.MoveToNextAttribute()); - nav.MoveToParent(); - } - - if (isDoc == false || key != keyMatch) - { - if (isDoc && nav.MoveToFirstChild()) - continue; - while (nav.MoveToNext(XPathNodeType.Element) == false) - if (nav.MoveToParent() == false || nav.NodeType == XPathNodeType.Root) return null; - continue; - } - - var elt = nav.UnderlyingObject as XmlNode; - return ConvertToDocument(elt, preview); - } - } - - public override IPublishedContent GetById(bool preview, Udi nodeId) - => throw new NotSupportedException(); - - public override bool HasById(bool preview, int contentId) - { - return GetXml(preview).CreateNavigator().MoveToId(contentId.ToString(CultureInfo.InvariantCulture)); - } - - public override IEnumerable GetAtRoot(bool preview, string culture = null) - { - return ConvertToDocuments(GetXml(preview).SelectNodes(XPathStrings.RootDocuments), preview); - } - - public override IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars) - { - if (xpath == null) throw new ArgumentNullException(nameof(xpath)); - if (string.IsNullOrWhiteSpace(xpath)) return null; - - var xml = GetXml(preview); - var node = vars == null - ? xml.SelectSingleNode(xpath) - : xml.SelectSingleNode(xpath, vars); - return ConvertToDocument(node, preview); - } - - public override IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) - { - if (xpath == null) throw new ArgumentNullException(nameof(xpath)); - - var xml = GetXml(preview); - var node = vars == null - ? xml.SelectSingleNode(xpath) - : xml.SelectSingleNode(xpath, vars); - return ConvertToDocument(node, preview); - } - - public override IEnumerable GetByXPath(bool preview, string xpath, XPathVariable[] vars) - { - if (xpath == null) throw new ArgumentNullException(nameof(xpath)); - if (string.IsNullOrWhiteSpace(xpath)) return Enumerable.Empty(); - - var xml = GetXml(preview); - var nodes = vars == null - ? xml.SelectNodes(xpath) - : xml.SelectNodes(xpath, vars); - return ConvertToDocuments(nodes, preview); - } - - public override IEnumerable GetByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) - { - if (xpath == null) throw new ArgumentNullException(nameof(xpath)); - - var xml = GetXml(preview); - var nodes = vars == null - ? xml.SelectNodes(xpath) - : xml.SelectNodes(xpath, vars); - return ConvertToDocuments(nodes, preview); - } - - public override bool HasContent(bool preview) - { - var xml = GetXml(preview); - var node = xml?.SelectSingleNode(XPathStrings.RootDocuments); - return node != null; - } - - public override XPathNavigator CreateNavigator(bool preview) - { - var xml = GetXml(preview); - return xml.CreateNavigator(); - } - - public override XPathNavigator CreateNodeNavigator(int id, bool preview) - { - // hackish - backward compatibility ;-( - - XPathNavigator navigator = null; - - if (preview) - { - var node = _xmlStore.GetPreviewXmlNode(id); - if (node != null) - { - navigator = node.CreateNavigator(); - } - } - else - { - var node = GetXml(false).GetElementById(id.ToInvariantString()); - if (node != null) - { - var doc = new XmlDocument(); - var clone = doc.ImportNode(node, false); - var props = node.SelectNodes("./* [not(@id)]"); - if (props == null) throw new Exception("oops"); - foreach (var n in props.Cast()) - clone.AppendChild(doc.ImportNode(n, true)); - navigator = node.CreateNavigator(); - } - } - - return navigator; - } - - #endregion - - #region Legacy Xml - - private readonly XmlStore _xmlStore; - private XmlDocument _xml; - private readonly PreviewContent _previewContent; - - internal XmlDocument GetXml(bool preview) - { - // not trying to be thread-safe here, that's not the point - - if (preview == false) - { - // if there's a current enlisted reader/writer, use its xml - var tempXml = _xmlStore.TempXml; - if (tempXml != null) return tempXml; - return _xml; - } - - // Xml cache does not support retrieving preview content when not previewing - if (_previewContent == null) - throw new InvalidOperationException("Cannot retrieve preview content when not previewing."); - - // PreviewContent tries to load the Xml once and if it fails, - // it invalidates itself and always return null for XmlContent. - var previewXml = _previewContent.XmlContent; - return previewXml ?? _xml; - } - - internal void Resync(XmlDocument xml) - { - _xml = xml; // re-capture - - // note: we're not resyncing "preview" because that would mean re-building the whole - // preview set which is costly, so basically when previewing, there will be no resync. - - // clear recursive properties cached by XmlPublishedContent.GetProperty - // assume that nothing else is going to cache IPublishedProperty items (else would need to do ByKeySearch) - // NOTE also clears all the media cache properties, which is OK (see media cache) - _appCache.ClearOfType(); - //_appCache.ClearCacheByKeySearch("XmlPublishedCache.PublishedContentCache:RecursiveProperty-"); - } - - #endregion - - #region XPathQuery - - static readonly char[] SlashChar = { '/' }; - - #endregion - - #region Content types - - public override IPublishedContentType GetContentType(int id) => _contentTypeCache.Get(PublishedItemType.Content, id); - - public override IPublishedContentType GetContentType(string alias) => _contentTypeCache.Get(PublishedItemType.Content, alias); - - public override IPublishedContentType GetContentType(Guid key) => _contentTypeCache.Get(PublishedItemType.Content, key); - - #endregion - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs deleted file mode 100644 index c8e5bfaacfce..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMediaCache.cs +++ /dev/null @@ -1,698 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.IO; -using System.Linq; -using System.Threading; -using System.Xml.XPath; -using Examine; -using Examine.Search; -using Lucene.Net.Store; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Core.Xml; -using Umbraco.Cms.Infrastructure.Examine; -using Umbraco.Extensions; -using Umbraco.Web.Composing; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - /// - /// An IPublishedMediaStore that first checks for the media in Examine, and then reverts to the database - /// - /// - /// NOTE: In the future if we want to properly cache all media this class can be extended or replaced when these classes/interfaces are exposed publicly. - /// - internal class PublishedMediaCache : PublishedCacheBase, IPublishedMediaCache - { - private readonly IMediaService _mediaService; - private readonly IUserService _userService; - - // by default these are null unless specified by the ctor dedicated to tests - // when they are null the cache derives them from the ExamineManager, see - // method GetExamineManagerSafe(). - // - private readonly ISearcher _searchProvider; - private readonly XmlStore _xmlStore; - private readonly PublishedContentTypeCache _contentTypeCache; - private readonly IEntityXmlSerializer _entitySerializer; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly IVariationContextAccessor _variationContextAccessor; - private readonly IExamineManager _examineManager = new ExamineManager(); - - // must be specified by the ctor - private readonly IAppCache _appCache; - - public PublishedMediaCache(XmlStore xmlStore, IMediaService mediaService, IUserService userService, - IAppCache appCache, PublishedContentTypeCache contentTypeCache, IEntityXmlSerializer entitySerializer, - IUmbracoContextAccessor umbracoContextAccessor, IVariationContextAccessor variationContextAccessor) - : base(false) - { - _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); - _userService = userService ?? throw new ArgumentNullException(nameof(userService)); - - _appCache = appCache; - _xmlStore = xmlStore; - _contentTypeCache = contentTypeCache; - _entitySerializer = entitySerializer; - _umbracoContextAccessor = umbracoContextAccessor; - _variationContextAccessor = variationContextAccessor; - } - - /// - /// Generally used for unit testing to use an explicit examine searcher - /// - /// - /// - /// - /// - /// - /// - internal PublishedMediaCache(IMediaService mediaService, IUserService userService, ISearcher searchProvider, IAppCache appCache, PublishedContentTypeCache contentTypeCache, IEntityXmlSerializer entitySerializer, IUmbracoContextAccessor umbracoContextAccessor) - : base(false) - { - _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); - _userService = userService ?? throw new ArgumentNullException(nameof(userService)); - _searchProvider = searchProvider ?? throw new ArgumentNullException(nameof(searchProvider)); - _appCache = appCache; - _contentTypeCache = contentTypeCache; - _entitySerializer = entitySerializer; - _umbracoContextAccessor = umbracoContextAccessor; - } - - static PublishedMediaCache() - { - InitializeCacheConfig(); - } - - public override IPublishedContent GetById(bool preview, int nodeId) - { - return GetUmbracoMedia(nodeId); - } - - public override IPublishedContent GetById(bool preview, Guid nodeId) - { - throw new NotImplementedException(); - } - - public override IPublishedContent GetById(bool preview, Udi nodeId) - => throw new NotSupportedException(); - - public override bool HasById(bool preview, int contentId) - { - return GetUmbracoMedia(contentId) != null; - } - - public override IEnumerable GetAtRoot(bool preview, string culture = null) - { - var searchProvider = GetSearchProviderSafe(); - - if (searchProvider != null) - { - try - { - // first check in Examine for the cache values - // +(+parentID:-1) +__IndexType:media - - var criteria = searchProvider.CreateQuery("media"); - var filter = criteria.ParentId(-1).Not().Field(UmbracoExamineFieldNames.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); - - var result = filter.Execute(); - if (result != null) - return result.Select(x => CreateFromCacheValues(ConvertFromSearchResult(x))); - } - catch (Exception ex) - { - if (ex is FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a load balanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - // TODO: Need to fix examine in LB scenarios! - Current.Logger.LogError(ex, "Could not load data from Examine index for media"); - } - else if (ex is ObjectDisposedException) - { - //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot - //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. - Current.Logger.LogError(ex, "Could not load data from Examine index for media, the app domain is most likely in a shutdown state"); - } - else throw; - } - } - - //something went wrong, fetch from the db - - var rootMedia = _mediaService.GetRootMedia(); - return rootMedia.Select(m => GetUmbracoMedia(m.Id)); - } - - public override IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //var navigator = CreateNavigator(preview); - //var iterator = navigator.Select(xpath, vars); - //return GetSingleByXPath(iterator); - } - - public override IPublishedContent GetSingleByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //var navigator = CreateNavigator(preview); - //var iterator = navigator.Select(xpath, vars); - //return GetSingleByXPath(iterator); - } - - private IPublishedContent GetSingleByXPath(XPathNodeIterator iterator) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //if (iterator.MoveNext() == false) return null; - //var idAttr = iterator.Current.GetAttribute("id", ""); - //int id; - //return int.TryParse(idAttr, out id) ? GetUmbracoMedia(id) : null; - } - - public override IEnumerable GetByXPath(bool preview, string xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //var navigator = CreateNavigator(preview); - //var iterator = navigator.Select(xpath, vars); - //return GetByXPath(iterator); - } - - public override IEnumerable GetByXPath(bool preview, XPathExpression xpath, XPathVariable[] vars) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //var navigator = CreateNavigator(preview); - //var iterator = navigator.Select(xpath, vars); - //return GetByXPath(iterator); - } - - private IEnumerable GetByXPath(XPathNodeIterator iterator) - { - while (iterator.MoveNext()) - { - var idAttr = iterator.Current.GetAttribute("id", ""); - int id; - if (int.TryParse(idAttr, out id)) - yield return GetUmbracoMedia(id); - } - } - - public override XPathNavigator CreateNavigator(bool preview) - { - throw new NotImplementedException("PublishedMediaCache does not support XPath."); - //var doc = _xmlStore.GetMediaXml(); - //return doc.CreateNavigator(); - } - - public override XPathNavigator CreateNodeNavigator(int id, bool preview) - { - // preview is ignored for media cache - - // this code is mostly used when replacing old media.ToXml() code, and that code - // stored the XML attached to the media itself - so for some time in memory - so - // unless we implement some sort of cache here, we're probably degrading perfs. - - XPathNavigator navigator = null; - var node = _xmlStore.GetMediaXmlNode(id); - if (node != null) - { - navigator = node.CreateNavigator(); - } - return navigator; - } - - public override bool HasContent(bool preview) { throw new NotImplementedException(); } - - private ISearcher GetSearchProviderSafe() - { - if (_searchProvider != null) - return _searchProvider; - - try - { - return _examineManager.TryGetIndex(Constants.UmbracoIndexes.InternalIndexName, out var index) ? index.GetSearcher() : null; - } - catch (FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a load balanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - // TODO: Need to fix examine in LB scenarios! - } - catch (NullReferenceException) - { - //This will occur when the search provider cannot be initialized. In newer examine versions the initialization is lazy and therefore - // the manager will return the singleton without throwing initialization errors, however if examine isn't configured correctly a null - // reference error will occur because the examine settings are null. - } - catch (ObjectDisposedException) - { - //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot - //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. - } - return null; - } - - private IPublishedContent GetUmbracoMedia(int id) - { - // this recreates an IPublishedContent and model each time - // it is called, but at least it should NOT hit the database - // nor Lucene each time, relying on the memory cache instead - - if (id <= 0) return null; // fail fast - - var cacheValues = GetCacheValues(id, GetUmbracoMediaCacheValues); - - return cacheValues == null ? null : CreateFromCacheValues(cacheValues); - } - - private CacheValues GetUmbracoMediaCacheValues(int id) - { - var searchProvider = GetSearchProviderSafe(); - - if (searchProvider != null) - { - try - { - // first check in Examine as this is WAY faster - // - // the filter will create a query like this: - // +(+__NodeId:3113 -__Path:-1,-21,*) +__IndexType:media - // - // note that since the use of the wildcard, it automatically escapes it in Lucene. - - var criteria = searchProvider.CreateQuery("media"); - var filter = criteria.Id(id.ToInvariantString()).Not().Field(UmbracoExamineFieldNames.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()); - - var result = filter.Execute().FirstOrDefault(); - if (result != null) return ConvertFromSearchResult(result); - } - catch (Exception ex) - { - if (ex is FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a load balanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - // TODO: Need to fix examine in LB scenarios! - Current.Logger.LogError(ex, "Could not load data from Examine index for media"); - } - else if (ex is ObjectDisposedException) - { - //If the app domain is shutting down and the site is under heavy load the index reader will be closed and it really cannot - //be re-opened since the app domain is shutting down. In this case we have no option but to try to load the data from the db. - Current.Logger.LogError(ex, "Could not load data from Examine index for media, the app domain is most likely in a shutdown state"); - } - else throw; - } - } - - // don't log a warning here, as it can flood the log in case of eg a media picker referencing a media - // that has been deleted, hence is not in the Examine index anymore (for a good reason). try to get - // the media from the service, first - var media = _mediaService.GetById(id); - if (media == null || media.Trashed) return null; // not found, ok - - // so, the media was not found in Examine's index *yet* it exists, which probably indicates that - // the index is corrupted. Or not up-to-date. Log a warning, but only once, and only if seeing the - // error more that a number of times. - - var miss = Interlocked.CompareExchange(ref _examineIndexMiss, 0, 0); // volatile read - if (miss < ExamineIndexMissMax && Interlocked.Increment(ref _examineIndexMiss) == ExamineIndexMissMax) - Current.Logger.LogWarning("Failed ({ExamineIndexMissMax} times) to retrieve medias from Examine index and had to load" - + " them from DB. This may indicate that the Examine index is corrupted.", ExamineIndexMissMax); - - return ConvertFromIMedia(media); - } - - private const int ExamineIndexMissMax = 10; - private int _examineIndexMiss; - - internal CacheValues ConvertFromXPathNodeIterator(XPathNodeIterator media, int id) - { - if (media?.Current != null) - { - return media.Current.Name.InvariantEquals("error") - ? null - : ConvertFromXPathNavigator(media.Current); - } - - Current.Logger.LogWarning("Could not retrieve media {MediaId} from Examine index or from legacy library.GetMedia method", id); - - return null; - } - - internal CacheValues ConvertFromSearchResult(ISearchResult searchResult) - { - // note: fixing fields in 7.x, removed by Shan for 8.0 - - return new CacheValues - { - Values = searchResult.Values, - FromExamine = true - }; - } - - internal CacheValues ConvertFromXPathNavigator(XPathNavigator xpath, bool forceNav = false) - { - if (xpath == null) throw new ArgumentNullException(nameof(xpath)); - - var values = new Dictionary { { "nodeName", xpath.GetAttribute("nodeName", "") } }; - values["nodeTypeAlias"] = xpath.Name; - - var result = xpath.SelectChildren(XPathNodeType.Element); - //add the attributes e.g. id, parentId etc - if (result.Current != null && result.Current.HasAttributes) - { - if (result.Current.MoveToFirstAttribute()) - { - //checking for duplicate keys because of the 'nodeTypeAlias' might already be added above. - if (values.ContainsKey(result.Current.Name) == false) - { - values[result.Current.Name] = result.Current.Value; - } - while (result.Current.MoveToNextAttribute()) - { - if (values.ContainsKey(result.Current.Name) == false) - { - values[result.Current.Name] = result.Current.Value; - } - } - result.Current.MoveToParent(); - } - } - // because, migration - if (values.ContainsKey("key") == false) - values["key"] = Guid.Empty.ToString(); - //add the user props - while (result.MoveNext()) - { - if (result.Current != null && result.Current.HasAttributes == false) - { - var value = result.Current.Value; - if (string.IsNullOrEmpty(value)) - { - if (result.Current.HasAttributes || result.Current.SelectChildren(XPathNodeType.Element).Count > 0) - { - value = result.Current.OuterXml; - } - } - values[result.Current.Name] = value; - } - } - - return new CacheValues - { - Values = values, - XPath = forceNav ? xpath : null // outside of tests we do NOT want to cache the navigator! - }; - } - - internal CacheValues ConvertFromIMedia(IMedia media) - { - var values = new Dictionary(); - - var creator = _userService.GetProfileById(media.CreatorId); - var creatorName = creator == null ? "" : creator.Name; - - values["id"] = media.Id.ToString(); - values["key"] = media.Key.ToString(); - values["parentID"] = media.ParentId.ToString(); - values["level"] = media.Level.ToString(); - values["creatorID"] = media.CreatorId.ToString(); - values["creatorName"] = creatorName; - values["writerID"] = media.CreatorId.ToString(); - values["writerName"] = creatorName; - values["template"] = "0"; - values["urlName"] = ""; - values["sortOrder"] = media.SortOrder.ToString(); - values["createDate"] = media.CreateDate.ToString("yyyy-MM-dd HH:mm:ss"); - values["updateDate"] = media.UpdateDate.ToString("yyyy-MM-dd HH:mm:ss"); - values["nodeName"] = media.Name; - values["path"] = media.Path; - values["nodeType"] = media.ContentType.Id.ToString(); - values["nodeTypeAlias"] = media.ContentType.Alias; - - // add the user props - foreach (var prop in media.Properties) - values[prop.Alias] = prop.GetValue()?.ToString(); - - return new CacheValues - { - Values = values - }; - } - - /// - /// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists - /// in the results, if it does not, then we'll have to revert to looking up in the db. - /// - /// - /// - /// - private IPublishedProperty GetProperty(DictionaryPublishedContent dd, string alias) - { - //lets check if the alias does not exist on the document. - //NOTE: Examine will not index empty values and we do not output empty XML Elements to the cache - either of these situations - // would mean that the property is missing from the collection whether we are getting the value from Examine or from the library media cache. - if (dd.Properties.All(x => x.Alias.InvariantEquals(alias) == false)) - { - return null; - } - - if (dd.LoadedFromExamine) - { - //We are going to check for a special field however, that is because in some cases we store a 'Raw' - //value in the index such as for xml/html. - var rawValue = dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(UmbracoExamineFieldNames.RawFieldPrefix + alias)); - return rawValue - ?? dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); - } - - //if its not loaded from examine, then just return the property - return dd.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); - } - - /// - /// A Helper methods to return the children for media whether it is based on examine or xml - /// - /// - /// - /// - private IEnumerable GetChildrenMedia(int parentId, XPathNavigator xpath = null) - { - // if there *is* a navigator, directly look it up - if (xpath != null) - { - return ToIPublishedContent(parentId, xpath); - } - - // otherwise, try examine first, then re-look it up - var searchProvider = GetSearchProviderSafe(); - - if (searchProvider != null) - { - try - { - //first check in Examine as this is WAY faster - var criteria = searchProvider.CreateQuery("media"); - - var filter = criteria.ParentId(parentId).Not().Field(UmbracoExamineFieldNames.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard()) - .OrderBy(new SortableField("sortOrder", SortType.Int)); - //the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene. - //+(+parentId:3113 -__Path:-1,-21,*) +__IndexType:media - - // sort with the Sort field (updated for 8.0) - var results = filter.Execute(); - - if (results.Any()) - { - // var medias = results.Select(ConvertFromSearchResult); - var medias = results.Select(x => - { - int nid; - if (int.TryParse(x["__NodeId"], out nid) == false && int.TryParse(x["NodeId"], out nid) == false) - throw new Exception("Failed to extract NodeId from search result."); - var cacheValues = GetCacheValues(nid, id => ConvertFromSearchResult(x)); - return CreateFromCacheValues(cacheValues); - }); - - return medias; - } - - //if there's no result then return null. Previously we defaulted back to library.GetMedia below - //but this will always get called for when we are getting descendants since many items won't have - //children and then we are hitting the database again! - //So instead we're going to rely on Examine to have the correct results like it should. - return Enumerable.Empty(); - } - catch (FileNotFoundException) - { - //Currently examine is throwing FileNotFound exceptions when we have a load balanced filestore and a node is published in umbraco - //See this thread: http://examine.cdodeplex.com/discussions/264341 - //Catch the exception here for the time being, and just fallback to GetMedia - } - } - - // falling back to get media - // was library.GetMedia which had its own cache, but MediaService *also* caches - // so, library.GetMedia is gone and now we directly work with MediaService - // (code below copied from what library was doing) - var media = _mediaService.GetById(parentId); - if (media == null) - { - return Enumerable.Empty(); - } - - var serialized = _entitySerializer.Serialize(media, true); - - var mediaIterator = serialized.CreateNavigator().Select("/"); - - return mediaIterator.Current == null - ? Enumerable.Empty() - : ToIPublishedContent(parentId, mediaIterator.Current); - } - - - internal IEnumerable ToIPublishedContent(int parentId, XPathNavigator xpath) - { - var mediaList = new List(); - - // this is so bad, really - var item = xpath.Select("//*[@id='" + parentId + "']"); - if (item.Current == null) - return Enumerable.Empty(); - var items = item.Current.SelectChildren(XPathNodeType.Element); - - // and this does not work, because... meh - //var q = "//* [@id='" + parentId + "']/* [@id]"; - //var items = xpath.Select(q); - - foreach (XPathNavigator itemm in items) - { - int id; - if (int.TryParse(itemm.GetAttribute("id", ""), out id) == false) - continue; // uh? - var captured = itemm; - var cacheValues = GetCacheValues(id, idd => ConvertFromXPathNavigator(captured)); - mediaList.Add(CreateFromCacheValues(cacheValues)); - } - - return mediaList; - } - - - internal void Resync() - { - // clear recursive properties cached by XmlPublishedContent.GetProperty - // assume that nothing else is going to cache IPublishedProperty items (else would need to do ByKeySearch) - // NOTE all properties cleared when clearing the content cache (see content cache) - //_appCache.ClearCacheObjectTypes(); - //_appCache.ClearCacheByKeySearch("XmlPublishedCache.PublishedMediaCache:RecursiveProperty-"); - } - - #region Content types - - public override IPublishedContentType GetContentType(int id) => _contentTypeCache.Get(PublishedItemType.Media, id); - - public override IPublishedContentType GetContentType(string alias) => _contentTypeCache.Get(PublishedItemType.Media, alias); - - public override IPublishedContentType GetContentType(Guid key) => _contentTypeCache.Get(PublishedItemType.Media, key); - - public override IEnumerable GetByContentType(IPublishedContentType contentType) - { - throw new NotSupportedException(); - } - - #endregion - - // REFACTORING - - // caching the basic atomic values - and the parent id - // but NOT caching actual parent nor children and NOT even - // the list of children ids - BUT caching the path - - internal class CacheValues - { - public IReadOnlyDictionary Values { get; set; } - public XPathNavigator XPath { get; set; } - public bool FromExamine { get; set; } - } - - public const string PublishedMediaCacheKey = "MediaCacheMeh."; - private const int PublishedMediaCacheTimespanSeconds = 4 * 60; // 4 mins - private static TimeSpan _publishedMediaCacheTimespan; - private static bool _publishedMediaCacheEnabled; - - private static void InitializeCacheConfig() - { - _publishedMediaCacheEnabled = true; - _publishedMediaCacheTimespan = TimeSpan.FromSeconds(PublishedMediaCacheTimespanSeconds); - } - - internal IPublishedContent CreateFromCacheValues(CacheValues cacheValues) - { - var content = new DictionaryPublishedContent( - cacheValues.Values, - parentId => parentId < 0 ? null : GetUmbracoMedia(parentId), - GetChildrenMedia, - GetProperty, - _appCache, - _variationContextAccessor, - _contentTypeCache, - cacheValues.XPath, // though, outside of tests, that should be null - cacheValues.FromExamine - ); - return content.CreateModel(Current.PublishedModelFactory); - } - - private static CacheValues GetCacheValues(int id, Func func) - { - if (_publishedMediaCacheEnabled == false) - return func(id); - - var cache = Current.AppCaches.RuntimeCache; - var key = PublishedMediaCacheKey + id; - return (CacheValues)cache.Get(key, () => func(id), _publishedMediaCacheTimespan); - } - - internal static void ClearCache(int id) - { - var cache = Current.AppCaches.RuntimeCache; - var sid = id.ToString(); - var key = PublishedMediaCacheKey + sid; - - // we do clear a lot of things... but the cache refresher is somewhat - // convoluted and it's hard to tell what to clear exactly ;-( - - // clear the parent - NOT (why?) - //var exist = (CacheValues) cache.GetCacheItem(key); - //if (exist != null) - // cache.ClearCacheItem(PublishedMediaCacheKey + GetValuesValue(exist.Values, "parentID")); - - // clear the item - cache.Clear(key); - - // clear all children - in case we moved and their path has changed - var fid = "/" + sid + "/"; - cache.ClearOfType((k, v) => - GetValuesValue(v.Values, "path", "__Path").Contains(fid)); - } - - private static string GetValuesValue(IReadOnlyDictionary d, params string[] keys) - { - string value = null; - var ignored = keys.Any(x => d.TryGetValue(x, out value)); - return value ?? ""; - } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMember.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMember.cs deleted file mode 100644 index a885af1475c1..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMember.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.Membership; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Services; -using Umbraco.Extensions; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - /// - /// Exposes a member object as IPublishedContent - /// - public sealed class PublishedMember : PublishedContentBase - { - private readonly IMember _member; - private readonly IMembershipUser _membershipUser; - private readonly IPublishedProperty[] _properties; - private readonly IPublishedContentType _publishedMemberType; - - public PublishedMember( - IMember member, - IPublishedContentType publishedMemberType, - IVariationContextAccessor variationContextAccessor) : base(variationContextAccessor) - { - _member = member ?? throw new ArgumentNullException(nameof(member)); - _membershipUser = member; - _publishedMemberType = publishedMemberType ?? throw new ArgumentNullException(nameof(publishedMemberType)); - - // RawValueProperty is used for two things here - // - for the 'map properties' thing that we should really get rid of - // - for populating properties that every member should always have, and that we force-create - // if they are not part of the member type properties - in which case they are created as - // simple raw properties - which are completely invariant - - var properties = new List(); - foreach (var propertyType in _publishedMemberType.PropertyTypes) - { - var property = _member.Properties[propertyType.Alias]; - if (property == null) continue; - - properties.Add(new RawValueProperty(propertyType, this, property.GetValue())); - } - EnsureMemberProperties(properties); - _properties = properties.ToArray(); - } - - #region Membership provider member properties - - public string Email => _membershipUser.Email; - - public string UserName => _membershipUser.Username; - - public string Comments => _membershipUser.Comments; - - public bool IsApproved => _membershipUser.IsApproved; - - public bool IsLockedOut => _membershipUser.IsLockedOut; - - public DateTime LastLockoutDate => _membershipUser.LastLockoutDate; - - public DateTime CreationDate => _membershipUser.CreateDate; - - public DateTime LastLoginDate => _membershipUser.LastLoginDate; - - public DateTime LastPasswordChangeDate => _membershipUser.LastPasswordChangeDate; - - #endregion - - #region IPublishedContent - - public override PublishedItemType ItemType => PublishedItemType.Member; - - public override bool IsDraft(string culture = null) => false; - - public override bool IsPublished(string culture = null) => true; - - public override IPublishedContent Parent => null; - - public override IEnumerable Children => Enumerable.Empty(); - - public override IEnumerable ChildrenForAllCultures => Enumerable.Empty(); - - public override IEnumerable Properties => _properties; - - public override IPublishedProperty GetProperty(string alias) - { - return _properties.FirstOrDefault(x => x.Alias.InvariantEquals(alias)); - } - - private void EnsureMemberProperties(List properties) - { - var aliases = properties.Select(x => x.Alias).ToList(); - - EnsureMemberProperty(properties, aliases, nameof(IMember.Email), Email); - EnsureMemberProperty(properties, aliases, nameof(IMember.Username), UserName); - EnsureMemberProperty(properties, aliases, nameof(IMember.Comments), Comments); - EnsureMemberProperty(properties, aliases, nameof(IMember.IsApproved), IsApproved); - EnsureMemberProperty(properties, aliases, nameof(IMember.IsLockedOut), IsLockedOut); - EnsureMemberProperty(properties, aliases, nameof(IMember.LastLockoutDate), LastLockoutDate); - EnsureMemberProperty(properties, aliases, nameof(IMember.CreateDate), CreateDate); - EnsureMemberProperty(properties, aliases, nameof(IMember.LastLoginDate), LastLoginDate); - EnsureMemberProperty(properties, aliases, nameof(IMember.LastPasswordChangeDate), LastPasswordChangeDate); - } - - private void EnsureMemberProperty(List properties, List aliases, string alias, object value) - { - // if the property already has a value, nothing to do - if (aliases.Contains(alias)) return; - - // if not a property type, ignore - var propertyType = ContentType.GetPropertyType(alias); - if (propertyType == null) return; - - // create a raw-value property - // note: throws if propertyType variations is not InvariantNeutral - var property = new RawValueProperty(propertyType, this, value); - properties.Add(property); - } - - public override IPublishedContentType ContentType => _publishedMemberType; - - public override int Id => _member.Id; - - public override Guid Key => _member.Key; - - public override int? TemplateId => throw new NotSupportedException(); - - public override int SortOrder => 0; - - public override string Name => _member.Name; - - public override IReadOnlyDictionary Cultures => throw new NotSupportedException(); - - public override string UrlSegment => throw new NotSupportedException(); - - public override int WriterId => _member.CreatorId; - - public override int CreatorId => _member.CreatorId; - - public override string Path => _member.Path; - - public override DateTime CreateDate => _member.CreateDate; - - public override DateTime UpdateDate => _member.UpdateDate; - - public override int Level => _member.Level; - - public DateTime LastPasswordChangedDate => throw new NotImplementedException(); - - #endregion - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs deleted file mode 100644 index 1faa0ee94809..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedMemberCache.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Extensions; -using Umbraco.Web.Composing; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - class PublishedMemberCache : IPublishedMemberCache - { - private readonly PublishedContentTypeCache _contentTypeCache; - private readonly IVariationContextAccessor _variationContextAccessor; - - public PublishedMemberCache(PublishedContentTypeCache contentTypeCache, IVariationContextAccessor variationContextAccessor) - { - _contentTypeCache = contentTypeCache; - _variationContextAccessor = variationContextAccessor; - } - - public IPublishedContent Get(IMember member) - { - var type = _contentTypeCache.Get(PublishedItemType.Member, member.ContentTypeId); - return new PublishedMember(member, type, _variationContextAccessor) - .CreateModel(Current.PublishedModelFactory); - } - - #region Content types - - public IPublishedContentType GetContentType(int id) => _contentTypeCache.Get(PublishedItemType.Member, id); - - public IPublishedContentType GetContentType(string alias) => _contentTypeCache.Get(PublishedItemType.Member, alias); - - #endregion - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshot.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshot.cs deleted file mode 100644 index 11abce0e645f..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/PublishedSnapshot.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.PublishedCache; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - /// - /// Implements a published snapshot. - /// - class PublishedSnapshot : IPublishedSnapshot - { - /// - /// Initializes a new instance of the class with a content cache - /// and a media cache. - /// - public PublishedSnapshot( - PublishedContentCache contentCache, - PublishedMediaCache mediaCache, - PublishedMemberCache memberCache, - DomainCache domainCache) - { - Content = contentCache; - Media = mediaCache; - Members = memberCache; - Domains = domainCache; - } - - /// - public IPublishedContentCache Content { get; } - - /// - public IPublishedMediaCache Media { get; } - - /// - public IPublishedMemberCache Members { get; } - - /// - public IDomainCache Domains { get; } - - /// - public IAppCache SnapshotCache => null; - - /// - public IAppCache ElementsCache => null; - - /// - public IDisposable ForcedPreview(bool preview, Action callback = null) - { - // the XML cache does not support forcing preview, really, so, just pretend... - return new ForcedPreviewObject(); - } - - private class ForcedPreviewObject : DisposableObjectSlim - { - protected override void DisposeResources() - { } - } - - public void Dispose() - { } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/RoutesCache.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/RoutesCache.cs deleted file mode 100644 index 71f2f421ffb4..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/RoutesCache.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - // Note: RoutesCache closely follows the caching strategy dating from v4, which - // is obviously broken in many ways (eg it's a global cache but relying to some - // extend to the content cache, which itself is local to each request...). - // Not going to fix it anyway. - - class RoutesCache - { - private ConcurrentDictionary _routes; - private ConcurrentDictionary _nodeIds; - - // NOTE - // RoutesCache is cleared by - // - ContentTypeCacheRefresher, whenever anything happens to any content type - // - DomainCacheRefresher, whenever anything happens to any domain - // - XmlStore, whenever anything happens to the XML cache - - /// - /// Initializes a new instance of the class. - /// - public RoutesCache() - { - Clear(); - } - - /// - /// Used ONLY for unit tests - /// - /// - internal IDictionary GetCachedRoutes() - { - return _routes; - } - - /// - /// Used ONLY for unit tests - /// - /// - internal IDictionary GetCachedIds() - { - return _nodeIds; - } - - #region Public - - /// - /// Stores a route for a node. - /// - /// The node identified. - /// The route. - /// A value indicating whether the value can be trusted for inbound routing. - public void Store(int nodeId, string route, bool trust) - { - _routes.AddOrUpdate(nodeId, i => route, (i, s) => route); - if (trust) - _nodeIds.AddOrUpdate(route, i => nodeId, (i, s) => nodeId); - } - - /// - /// Gets a route for a node. - /// - /// The node identifier. - /// The route for the node, else null. - public string GetRoute(int nodeId) - { - string val; - _routes.TryGetValue(nodeId, out val); - return val; - } - - /// - /// Gets a node for a route. - /// - /// The route. - /// The node identified for the route, else zero. - public int GetNodeId(string route) - { - int val; - _nodeIds.TryGetValue(route, out val); - return val; - } - - /// - /// Clears the route for a node. - /// - /// The node identifier. - public void ClearNode(int nodeId) - { - string route; - if (_routes.TryRemove(nodeId, out route)) - { - int id; - _nodeIds.TryRemove(route, out id); - } - } - - /// - /// Clears all routes. - /// - public void Clear() - { - _routes = new ConcurrentDictionary(); - _nodeIds = new ConcurrentDictionary(); - } - - #endregion - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs deleted file mode 100644 index fe560e4c93d0..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/SafeXmlReaderWriter.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System; -using System.Xml; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Scoping; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - // TODO: should be a ScopeContextualBase - internal class SafeXmlReaderWriter : IDisposable - { - private readonly bool _scoped; - private readonly Action _refresh; - private readonly Action _apply; - private IDisposable _releaser; - private bool _applyChanges; - private XmlDocument _xml, _origXml; - private bool _using; - private bool _registerXmlChange; - - // the default enlist priority is 100 - // enlist with a lower priority to ensure that anything "default" has a clean xml - private const int EnlistPriority = 60; - private const string EnlistKey = "safeXmlReaderWriter"; - - private SafeXmlReaderWriter(IDisposable releaser, XmlDocument xml, Action refresh, Action apply, bool isWriter, bool scoped) - { - _releaser = releaser; - _refresh = refresh; - _apply = apply; - _scoped = scoped; - - IsWriter = isWriter; - - _xml = IsWriter ? Clone(xml) : xml; - } - - public static SafeXmlReaderWriter Get(IScopeProvider scopeProvider) - { - return scopeProvider?.Context?.GetEnlisted(EnlistKey); - } - - public static SafeXmlReaderWriter Get(IScopeProvider scopeProvider, SystemLock xmlLock, XmlDocument xml, Action refresh, Action apply, bool writer) - { - var scopeContext = scopeProvider.Context; - - // no real scope = just create a reader/writer instance - if (scopeContext == null) - { - // obtain exclusive access to xml and create reader/writer - var releaser = xmlLock.Lock(); - return new SafeXmlReaderWriter(releaser, xml, refresh, apply, writer, false); - } - - // get or create an enlisted reader/writer - var rw = scopeContext.Enlist(EnlistKey, - () => // creator - { - // obtain exclusive access to xml and create reader/writer - var releaser = xmlLock.Lock(); - return new SafeXmlReaderWriter(releaser, xml, refresh, apply, writer, true); - }, - (completed, item) => // action - { - item.DisposeForReal(completed); - }, EnlistPriority); - - // ensure it's not already in-use - should never happen, just being super safe - if (rw._using) - throw new InvalidOperationException("panic: used."); - rw._using = true; - - return rw; - } - - public bool IsWriter { get; private set; } - - public void UpgradeToWriter(bool auto) - { - if (IsWriter) - throw new InvalidOperationException("Already a writer."); - IsWriter = true; - - _xml = Clone(_xml); - } - - // for tests - internal static Action Cloning { get; set; } - - private XmlDocument Clone(XmlDocument xml) - { - Cloning?.Invoke(); - if (_origXml != null) - throw new Exception("panic."); - _origXml = xml; - return (XmlDocument) xml?.CloneNode(true); - } - - public XmlDocument Xml - { - get => _xml; - set - { - if (IsWriter == false) - throw new InvalidOperationException("Not a writer."); - _xml = value; - } - } - - // registerXmlChange indicates whether to do what should be done when Xml changes, - // that is, to request that the file be written to disk - something we don't want - // to do if we're committing Xml precisely after we've read from disk! - public void AcceptChanges(bool registerXmlChange = true) - { - if (IsWriter == false) - throw new InvalidOperationException("Not a writer."); - - _applyChanges = true; - _registerXmlChange |= registerXmlChange; - } - - private void DisposeForReal(bool completed) - { - if (IsWriter) - { - // apply changes, or restore the original xml for the current request - if (_applyChanges && completed) - _apply(_xml, _registerXmlChange); - else - _refresh(_origXml); - } - - // release the lock - _releaser.Dispose(); - _releaser = null; - } - - public void Dispose() - { - _using = false; - - if (_scoped == false) - { - // really dispose - DisposeForReal(true); - } - else - { - // don't really dispose, - // just apply the changes for the current request - _refresh(_xml); - } - } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs deleted file mode 100644 index d1a3340b067f..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/UmbracoContextCache.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Concurrent; -using System.Runtime.CompilerServices; -using Umbraco.Cms.Core.Web; -using Umbraco.Web; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - static class UmbracoContextCache - { - static readonly ConditionalWeakTable> Caches - = new ConditionalWeakTable>(); - - public static ConcurrentDictionary Current - { - get - { - var umbracoContext = Umbraco.Web.Composing.Current.UmbracoContext; - - // will get or create a value - // a ConditionalWeakTable is thread-safe - // does not prevent the context from being disposed, and then the dictionary will be disposed too - return umbracoContext == null ? null : Caches.GetOrCreateValue(umbracoContext); - } - } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs deleted file mode 100644 index aa90f0dbfa03..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedContent.cs +++ /dev/null @@ -1,441 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Xml; -using System.Xml.Serialization; -using System.Xml.XPath; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Extensions; -using Umbraco.Web.Composing; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - /// - /// Represents an IPublishedContent which is created based on an Xml structure. - /// - [Serializable] - [XmlType(Namespace = "http://umbraco.org/webservices/")] - internal class XmlPublishedContent : PublishedContentBase - { - private XmlPublishedContent( - XmlNode xmlNode, - bool isPreviewing, - IAppCache appCache, - PublishedContentTypeCache contentTypeCache, - IVariationContextAccessor variationContextAccessor): base(variationContextAccessor) - { - _xmlNode = xmlNode; - _isPreviewing = isPreviewing; - - _appCache = appCache; - _contentTypeCache = contentTypeCache; - _variationContextAccessor = variationContextAccessor; - } - - private readonly XmlNode _xmlNode; - private readonly bool _isPreviewing; - private readonly IAppCache _appCache; // at snapshot/request level (see PublishedContentCache) - private readonly PublishedContentTypeCache _contentTypeCache; - private readonly IVariationContextAccessor _variationContextAccessor; - - private readonly object _initializeLock = new object(); - - private bool _nodeInitialized; - private bool _parentInitialized; - private bool _childrenInitialized; - - private IEnumerable _children = Enumerable.Empty(); - private IPublishedContent _parent; - - private IPublishedContentType _contentType; - private Dictionary _properties; - - private int _id; - private Guid _key; - private int _template; - private string _name; - private string _docTypeAlias; - private int _docTypeId; - private int _writerId; - private int _creatorId; - private string _urlName; - private string _path; - private DateTime _createDate; - private DateTime _updateDate; - private int _sortOrder; - private int _level; - private bool _isDraft; - - - public override IEnumerable Children - { - get - { - EnsureNodeInitialized(andChildren: true); - return _children; - } - } - - public override IEnumerable ChildrenForAllCultures => Children; - - public override IPublishedProperty GetProperty(string alias) - { - EnsureNodeInitialized(); - IPublishedProperty property; - return _properties.TryGetValue(alias, out property) ? property : null; - } - - public override PublishedItemType ItemType => PublishedItemType.Content; - - public override IPublishedContent Parent - { - get - { - EnsureNodeInitialized(andParent: true); - return _parent; - } - } - - public override int Id - { - get - { - EnsureNodeInitialized(); - return _id; - } - } - - public override Guid Key - { - get - { - EnsureNodeInitialized(); - return _key; - } - } - - public override int? TemplateId - { - get - { - EnsureNodeInitialized(); - return _template; - } - } - - public override int SortOrder - { - get - { - EnsureNodeInitialized(); - return _sortOrder; - } - } - - public override string Name - { - get - { - EnsureNodeInitialized(); - return _name; - } - } - - private Dictionary _cultures; - - private Dictionary GetCultures() - { - EnsureNodeInitialized(); - return new Dictionary { { "", new PublishedCultureInfo("", _name, _urlName, _updateDate) } }; - } - - public override IReadOnlyDictionary Cultures => _cultures ?? (_cultures = GetCultures()); - - public override int WriterId - { - get - { - EnsureNodeInitialized(); - return _writerId; - } - } - - public override int CreatorId - { - get - { - EnsureNodeInitialized(); - return _creatorId; - } - } - - public override string Path - { - get - { - EnsureNodeInitialized(); - return _path; - } - } - - public override DateTime CreateDate - { - get - { - EnsureNodeInitialized(); - return _createDate; - } - } - - public override DateTime UpdateDate - { - get - { - EnsureNodeInitialized(); - return _updateDate; - } - } - - public override string UrlSegment - { - get - { - EnsureNodeInitialized(); - return _urlName; - } - } - - public override int Level - { - get - { - EnsureNodeInitialized(); - return _level; - } - } - - public override bool IsDraft(string culture = null) - { - EnsureNodeInitialized(); - return _isDraft; // bah - } - - public override bool IsPublished(string culture = null) - { - EnsureNodeInitialized(); - return true; // Intentionally not implemented, because the XmlPublishedContent should not support this. - } - - public override IEnumerable Properties - { - get - { - EnsureNodeInitialized(); - return _properties.Values; - } - } - - public override IPublishedContentType ContentType - { - get - { - EnsureNodeInitialized(); - return _contentType; - } - } - - private void InitializeParent() - { - var parent = _xmlNode?.ParentNode; - if (parent == null) return; - - if (parent.Attributes?.GetNamedItem("isDoc") != null) - _parent = Get(parent, _isPreviewing, _appCache, _contentTypeCache, _variationContextAccessor); - - _parentInitialized = true; - } - - private void EnsureNodeInitialized(bool andChildren = false, bool andParent = false) - { - // In *theory* XmlPublishedContent are a per-request thing, and so should not - // end up being involved into multi-threaded situations - however, it's been - // reported that some users ended up seeing 100% CPU due to infinite loops in - // the properties dictionary in InitializeNode, which would indicate that the - // dictionary *is* indeed involved in some multi-threaded operation. No idea - // what users are doing that cause this, but let's be friendly and use a true - // lock around initialization. - - lock (_initializeLock) - { - if (_nodeInitialized == false) InitializeNode(); - if (andChildren && _childrenInitialized == false) InitializeChildren(); - if (andParent && _parentInitialized == false) InitializeParent(); - } - } - - private void InitializeNode() - { - InitializeNode(this, _xmlNode, _isPreviewing, - out _id, out _key, out _template, out _sortOrder, out _name, - out _urlName, out _creatorId, out _writerId, out _docTypeAlias, out _docTypeId, out _path, - out _createDate, out _updateDate, out _level, out _isDraft, out _contentType, out _properties, - _contentTypeCache.Get); - - _nodeInitialized = true; - } - - // internal for some benchmarks - internal static void InitializeNode(XmlPublishedContent node, XmlNode xmlNode, bool isPreviewing, - out int id, out Guid key, out int template, out int sortOrder, out string name, out string urlName, - out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path, - out DateTime createDate, out DateTime updateDate, out int level, out bool isDraft, - out IPublishedContentType contentType, out Dictionary properties, - Func getPublishedContentType) - { - //initialize the out params with defaults: - docTypeAlias = null; - id = template = sortOrder = template = creatorId = writerId = docTypeId = level = default(int); - key = default(Guid); - name = docTypeAlias = urlName = path = null; - createDate = updateDate = default(DateTime); - isDraft = false; - contentType = null; - properties = null; - - if (xmlNode == null) return; - - if (xmlNode.Attributes != null) - { - id = int.Parse(xmlNode.Attributes.GetNamedItem("id").Value); - if (xmlNode.Attributes.GetNamedItem("key") != null) // because, migration - key = Guid.Parse(xmlNode.Attributes.GetNamedItem("key").Value); - if (xmlNode.Attributes.GetNamedItem("template") != null) - template = int.Parse(xmlNode.Attributes.GetNamedItem("template").Value); - if (xmlNode.Attributes.GetNamedItem("sortOrder") != null) - sortOrder = int.Parse(xmlNode.Attributes.GetNamedItem("sortOrder").Value); - if (xmlNode.Attributes.GetNamedItem("nodeName") != null) - name = xmlNode.Attributes.GetNamedItem("nodeName").Value; - if (xmlNode.Attributes.GetNamedItem("urlName") != null) - urlName = xmlNode.Attributes.GetNamedItem("urlName").Value; - - //Added the actual userID, as a user cannot be looked up via full name only... - if (xmlNode.Attributes.GetNamedItem("creatorID") != null) - creatorId = int.Parse(xmlNode.Attributes.GetNamedItem("creatorID").Value); - if (xmlNode.Attributes.GetNamedItem("writerID") != null) - writerId = int.Parse(xmlNode.Attributes.GetNamedItem("writerID").Value); - - docTypeAlias = xmlNode.Name; - - if (xmlNode.Attributes.GetNamedItem("nodeType") != null) - docTypeId = int.Parse(xmlNode.Attributes.GetNamedItem("nodeType").Value); - if (xmlNode.Attributes.GetNamedItem("path") != null) - path = xmlNode.Attributes.GetNamedItem("path").Value; - if (xmlNode.Attributes.GetNamedItem("createDate") != null) - createDate = DateTime.Parse(xmlNode.Attributes.GetNamedItem("createDate").Value); - if (xmlNode.Attributes.GetNamedItem("updateDate") != null) - updateDate = DateTime.Parse(xmlNode.Attributes.GetNamedItem("updateDate").Value); - if (xmlNode.Attributes.GetNamedItem("level") != null) - level = int.Parse(xmlNode.Attributes.GetNamedItem("level").Value); - - isDraft = xmlNode.Attributes.GetNamedItem("isDraft") != null; - } - - //dictionary to store the property node data - var propertyNodes = new Dictionary(); - - foreach (XmlNode n in xmlNode.ChildNodes) - { - var e = n as XmlElement; - if (e == null) continue; - if (e.HasAttribute("isDoc") == false) - { - PopulatePropertyNodes(propertyNodes, e, false); - } - else break; //we are not longer on property elements - } - - //lookup the content type and create the properties collection - try - { - contentType = getPublishedContentType(PublishedItemType.Content, docTypeAlias); - - } - catch (InvalidOperationException e) - { - // TODO: enable! - //content.Instance.RefreshContentFromDatabase(); - throw new InvalidOperationException($"{e.Message}. This usually indicates that the content cache is corrupt; the content cache has been rebuilt in an attempt to self-fix the issue."); - } - - //fill in the property collection - properties = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var propertyType in contentType.PropertyTypes) - { - var val = propertyNodes.TryGetValue(propertyType.Alias.ToLowerInvariant(), out XmlNode n) - ? new XmlPublishedProperty(propertyType, node, isPreviewing, n) - : new XmlPublishedProperty(propertyType, node, isPreviewing); - - properties[propertyType.Alias] = val; - } - } - - private static void PopulatePropertyNodes(IDictionary propertyNodes, XmlNode n, bool legacy) - { - var attrs = n.Attributes; - if (attrs == null) return; - - var alias = legacy - ? attrs.GetNamedItem("alias").Value - : n.Name; - propertyNodes[alias.ToLowerInvariant()] = n; - } - - private void InitializeChildren() - { - if (_xmlNode == null) return; - - // load children - const string childXPath = "* [@isDoc]"; - var nav = _xmlNode.CreateNavigator(); - var expr = nav.Compile(childXPath); - //expr.AddSort("@sortOrder", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Number); - var iterator = nav.Select(expr); - - _children = iterator.Cast() - .Select(n => Get(((IHasXmlNode) n).GetNode(), _isPreviewing, _appCache, _contentTypeCache, _variationContextAccessor)) - .OrderBy(x => x.SortOrder) - .ToList(); - - _childrenInitialized = true; - } - - /// - /// Gets an IPublishedContent corresponding to an Xml cache node. - /// - /// The Xml node. - /// A value indicating whether we are previewing or not. - /// A cache. - /// A content type cache. - /// A umbraco context accessor - /// - /// The IPublishedContent corresponding to the Xml cache node. - /// Maintains a per-request cache of IPublishedContent items in order to make - /// sure that we create only one instance of each for the duration of a request. The - /// returned IPublishedContent is a model, if models are enabled. - public static IPublishedContent Get(XmlNode node, bool isPreviewing, IAppCache appCache, - PublishedContentTypeCache contentTypeCache, IVariationContextAccessor variationContextAccessor) - { - // only 1 per request - - var attrs = node.Attributes; - var id = attrs?.GetNamedItem("id").Value; - if (id.IsNullOrWhiteSpace()) throw new InvalidOperationException("Node has no ID attribute."); - var key = CacheKeyPrefix + id; // dont bother with preview, wont change during request in Xml cache - return (IPublishedContent) appCache.Get(key, () => (new XmlPublishedContent(node, isPreviewing, appCache, contentTypeCache, variationContextAccessor)).CreateModel(Current.PublishedModelFactory)); - } - - private const string CacheKeyPrefix = "CONTENTCACHE_XMLPUBLISHEDCONTENT_"; - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedProperty.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedProperty.cs deleted file mode 100644 index d0e71e782951..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedProperty.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Xml; -using System.Xml.Serialization; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Xml; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - /// - /// Represents an IDocumentProperty which is created based on an Xml structure. - /// - [Serializable] - [XmlType(Namespace = "http://umbraco.org/webservices/")] - internal class XmlPublishedProperty : PublishedPropertyBase - { - private readonly string _sourceValue; // the raw, xml node value - - // Xml cache not using XPath value... and as for the rest... - // we're single threaded here, keep it simple - private object _objectValue; - private bool _objectValueComputed; - private readonly bool _isPreviewing; - private readonly IPublishedContent _content; - - /// - /// Gets the raw value of the property. - /// - public override object GetSourceValue(string culture = null, string segment = null) => _sourceValue; - - // in the Xml cache, everything is a string, and to have a value - // you want to have a non-null, non-empty string. - public override bool HasValue(string culture = null, string segment = null) => _sourceValue.Trim().Length > 0; - - public override object GetValue(string culture = null, string segment = null) - { - // NOT caching the source (intermediate) value since we'll never need it - // everything in Xml cache is per-request anyways - // also, properties should not be shared between requests and therefore - // are single threaded, so the following code should be safe & fast - - if (_objectValueComputed) return _objectValue; - var inter = PropertyType.ConvertSourceToInter(_content, _sourceValue, _isPreviewing); - // initial reference cache level always is .Content - _objectValue = PropertyType.ConvertInterToObject(_content, PropertyCacheLevel.Element, inter, _isPreviewing); - _objectValueComputed = true; - return _objectValue; - } - - public override object GetXPathValue(string culture = null, string segment = null) { throw new NotImplementedException(); } - - public XmlPublishedProperty(IPublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, XmlNode propertyXmlData) - : this(propertyType, content, isPreviewing) - { - if (propertyXmlData == null) - throw new ArgumentNullException(nameof(propertyXmlData), "Property xml source is null"); - _sourceValue = XmlHelper.GetNodeValue(propertyXmlData); - } - - public XmlPublishedProperty(IPublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing, string propertyData) - : this(propertyType, content, isPreviewing) - { - if (propertyData == null) - throw new ArgumentNullException(nameof(propertyData)); - _sourceValue = propertyData; - } - - public XmlPublishedProperty(IPublishedPropertyType propertyType, IPublishedContent content, bool isPreviewing) - : base(propertyType, PropertyCacheLevel.Unknown) // cache level is ignored - { - _sourceValue = string.Empty; - _content = content; - _isPreviewing = isPreviewing; - } - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs deleted file mode 100644 index 580a4078f8fd..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlPublishedSnapshotService.cs +++ /dev/null @@ -1,222 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Persistence.Repositories; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Runtime; -using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Core.Web; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - /// - /// Implements a published snapshot service. - /// - internal class XmlPublishedSnapshotService : IPublishedSnapshotService - { - private readonly XmlStore _xmlStore; - private readonly RoutesCache _routesCache; - private readonly IPublishedContentTypeFactory _publishedContentTypeFactory; - private readonly PublishedContentTypeCache _contentTypeCache; - private readonly IDomainService _domainService; - private readonly IMediaService _mediaService; - private readonly IUserService _userService; - private readonly IAppCache _requestCache; - private readonly GlobalSettings _globalSettings; - private readonly IDefaultCultureAccessor _defaultCultureAccessor; - private readonly IEntityXmlSerializer _entitySerializer; - private readonly IVariationContextAccessor _variationContextAccessor; - private readonly IUmbracoContextAccessor _umbracoContextAccessor; - - #region Constructors - - // used in WebBootManager + tests - public XmlPublishedSnapshotService( - ServiceContext serviceContext, - IPublishedContentTypeFactory publishedContentTypeFactory, - IScopeProvider scopeProvider, - IAppCache requestCache, - IPublishedSnapshotAccessor publishedSnapshotAccessor, - IVariationContextAccessor variationContextAccessor, - IUmbracoContextAccessor umbracoContextAccessor, - IDocumentRepository documentRepository, - IMediaRepository mediaRepository, - IMemberRepository memberRepository, - IDefaultCultureAccessor defaultCultureAccessor, - ILoggerFactory loggerFactory, - GlobalSettings globalSettings, - IHostingEnvironment hostingEnvironment, - IApplicationShutdownRegistry hostingLifetime, - IShortStringHelper shortStringHelper, - IEntityXmlSerializer entitySerializer, - MainDom mainDom, - bool testing = false, - bool enableRepositoryEvents = true) - : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, - publishedSnapshotAccessor, variationContextAccessor, umbracoContextAccessor, - documentRepository, mediaRepository, memberRepository, - defaultCultureAccessor, - loggerFactory, globalSettings, hostingEnvironment, hostingLifetime, shortStringHelper, entitySerializer, null, mainDom, testing, enableRepositoryEvents) - { - _umbracoContextAccessor = umbracoContextAccessor; - } - - // used in some tests - internal XmlPublishedSnapshotService( - ServiceContext serviceContext, - IPublishedContentTypeFactory publishedContentTypeFactory, - IScopeProvider scopeProvider, - IAppCache requestCache, - IPublishedSnapshotAccessor publishedSnapshotAccessor, - IVariationContextAccessor variationContextAccessor, - IUmbracoContextAccessor umbracoContextAccessor, - IDocumentRepository documentRepository, - IMediaRepository mediaRepository, - IMemberRepository memberRepository, - IDefaultCultureAccessor defaultCultureAccessor, - ILoggerFactory loggerFactory, - GlobalSettings globalSettings, - IHostingEnvironment hostingEnvironment, - IApplicationShutdownRegistry hostingLifetime, - IShortStringHelper shortStringHelper, - IEntityXmlSerializer entitySerializer, - PublishedContentTypeCache contentTypeCache, - MainDom mainDom, - bool testing, - bool enableRepositoryEvents) - { - _routesCache = new RoutesCache(); - _publishedContentTypeFactory = publishedContentTypeFactory; - _contentTypeCache = contentTypeCache - ?? new PublishedContentTypeCache(serviceContext.ContentTypeService, serviceContext.MediaTypeService, serviceContext.MemberTypeService, publishedContentTypeFactory, loggerFactory.CreateLogger()); - - _xmlStore = new XmlStore(serviceContext.ContentTypeService, serviceContext.ContentService, scopeProvider, _routesCache, - _contentTypeCache, publishedSnapshotAccessor, mainDom, testing, enableRepositoryEvents, - documentRepository, mediaRepository, memberRepository, entitySerializer, hostingEnvironment, hostingLifetime, shortStringHelper); - - _domainService = serviceContext.DomainService; - _mediaService = serviceContext.MediaService; - _userService = serviceContext.UserService; - _defaultCultureAccessor = defaultCultureAccessor; - _variationContextAccessor = variationContextAccessor; - _requestCache = requestCache; - _umbracoContextAccessor = umbracoContextAccessor; - _globalSettings = globalSettings; - _entitySerializer = entitySerializer; - } - - public void Dispose() - { - _xmlStore.Dispose(); - } - - #endregion - - public IPublishedSnapshot CreatePublishedSnapshot(string previewToken) - { - // use _requestCache to store recursive properties lookup, etc. both in content - // and media cache. Life span should be the current request. Or, ideally - // the current caches, but that would mean creating an extra cache (StaticCache - // probably) so better use RequestCache. - - var domainCache = new DomainCache(_domainService, _defaultCultureAccessor); - - return new PublishedSnapshot( - new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _contentTypeCache, _routesCache, _variationContextAccessor, previewToken), - new PublishedMediaCache(_xmlStore, _mediaService, _userService, _requestCache, _contentTypeCache, _entitySerializer, _umbracoContextAccessor, _variationContextAccessor), - new PublishedMemberCache(_contentTypeCache, _variationContextAccessor), - domainCache); - } - - #region Xml specific - - /// - /// Gets the underlying XML store. - /// - public XmlStore XmlStore => _xmlStore; - - /// - /// Gets the underlying RoutesCache. - /// - public RoutesCache RoutesCache => _routesCache; - - public bool VerifyContentAndPreviewXml() - { - return XmlStore.VerifyContentAndPreviewXml(); - } - - public void RebuildContentAndPreviewXml() - { - XmlStore.RebuildContentAndPreviewXml(); - } - - public bool VerifyMediaXml() - { - return XmlStore.VerifyMediaXml(); - } - - public void RebuildMediaXml() - { - XmlStore.RebuildMediaXml(); - } - - public bool VerifyMemberXml() - { - return XmlStore.VerifyMemberXml(); - } - - public void RebuildMemberXml() - { - XmlStore.RebuildMemberXml(); - } - - #endregion - - #region Change management - - public void Notify(ContentCacheRefresher.JsonPayload[] payloads, out bool draftChanged, out bool publishedChanged) - { - _xmlStore.Notify(payloads, out draftChanged, out publishedChanged); - } - - public void Notify(MediaCacheRefresher.JsonPayload[] payloads, out bool anythingChanged) - { - foreach (var payload in payloads) - PublishedMediaCache.ClearCache(payload.Id); - - anythingChanged = true; - } - - public void Notify(ContentTypeCacheRefresher.JsonPayload[] payloads) - { - _xmlStore.Notify(payloads); - if (payloads.Any(x => x.ItemType == typeof(IContentType).Name)) - _routesCache.Clear(); - } - - public void Notify(DataTypeCacheRefresher.JsonPayload[] payloads) - { - _publishedContentTypeFactory.NotifyDataTypeChanges(payloads.Select(x => x.Id).ToArray()); - _xmlStore.Notify(payloads); - } - - public void Notify(DomainCacheRefresher.JsonPayload[] payloads) - { - _routesCache.Clear(); - } - - #endregion - - public void Rebuild(int groupSize = 5000, IReadOnlyCollection contentTypeIds = null, IReadOnlyCollection mediaTypeIds = null, IReadOnlyCollection memberTypeIds = null) { } - - public Task CollectAsync() => Task.CompletedTask; - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs deleted file mode 100644 index af0848010d50..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlStore.cs +++ /dev/null @@ -1,2058 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Xml; -using Microsoft.Extensions.Logging; -using Moq; -using NPoco; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Notifications; -using Umbraco.Cms.Core.Persistence.Repositories; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Runtime; -using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Changes; -using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Core.Xml; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.Composing; -using Umbraco.Web.Scheduling; -using File = System.IO.File; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - /// - /// Represents the Xml storage for the Xml published cache. - /// - /// - /// One instance of is instantiated by the and - /// then passed to all instances that are created (one per request). - /// This class should *not* be public. - /// - internal class XmlStore : - IDisposable, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler - { - private readonly IDocumentRepository _documentRepository; - private readonly IMediaRepository _mediaRepository; - private readonly IMemberRepository _memberRepository; - private readonly IEntityXmlSerializer _entitySerializer; - private readonly IApplicationShutdownRegistry _hostingLifetime; - private readonly IShortStringHelper _shortStringHelper; - private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; - private readonly PublishedContentTypeCache _contentTypeCache; - private readonly RoutesCache _routesCache; - private readonly IContentTypeService _contentTypeService; - private readonly IContentService _contentService; - private readonly IScopeProvider _scopeProvider; - - private XmlStoreFilePersister _persisterTask; - private volatile bool _released; - - #region Constructors - - /// - /// Initializes a new instance of the class. - /// - /// The default constructor will boot the cache, load data from file or database, /// wire events in order to manage changes, etc. - public XmlStore(IContentTypeService contentTypeService, IContentService contentService, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, - IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IEntityXmlSerializer entitySerializer, IHostingEnvironment hostingEnvironment, IApplicationShutdownRegistry hostingLifetime, IShortStringHelper shortStringHelper) - : this(contentTypeService, contentService, scopeProvider, routesCache, contentTypeCache, publishedSnapshotAccessor, mainDom, false, false, documentRepository, mediaRepository, memberRepository, entitySerializer, hostingEnvironment, hostingLifetime, shortStringHelper) - { } - - // internal for unit tests - // no file nor db, no config check - // TODO: er, we DO have a DB? - internal XmlStore(IContentTypeService contentTypeService, IContentService contentService, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, - IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, - bool testing, bool enableRepositoryEvents, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IEntityXmlSerializer entitySerializer, IHostingEnvironment hostingEnvironment, IApplicationShutdownRegistry hostingLifetime, IShortStringHelper shortStringHelper) - { - if (testing == false) - EnsureConfigurationIsValid(); - - _contentTypeService = contentTypeService; - _contentService = contentService; - _scopeProvider = scopeProvider; - _routesCache = routesCache; - _contentTypeCache = contentTypeCache; - _publishedSnapshotAccessor = publishedSnapshotAccessor; - _documentRepository = documentRepository; - _mediaRepository = mediaRepository; - _memberRepository = memberRepository; - _entitySerializer = entitySerializer; - _hostingLifetime = hostingLifetime; - _shortStringHelper = shortStringHelper; - - _xmlFileName = TestHelper.IOHelper.MapPath(SystemFiles.GetContentCacheXml(hostingEnvironment)); - - if (testing) - { - _xmlFileEnabled = false; - } - else - { - InitializeFilePersister(mainDom); - } - - Initialize(testing, enableRepositoryEvents); - } - - // internal for unit tests - // initialize with an xml document - // no events, no file nor db, no config check - internal XmlStore(XmlDocument xmlDocument, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IHostingEnvironment hostingEnvironment) - { - _xmlDocument = xmlDocument; - _documentRepository = documentRepository; - _mediaRepository = mediaRepository; - _memberRepository = memberRepository; - _xmlFileEnabled = false; - _xmlFileName = TestHelper.IOHelper.MapPath(SystemFiles.GetContentCacheXml(hostingEnvironment)); - // do not plug events, we may not have what it takes to handle them - } - - // internal for unit tests - // initialize with a function returning an xml document - // no events, no file nor db, no config check - internal XmlStore(Func getXmlDocument, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IHostingEnvironment hostingEnvironment) - { - _documentRepository = documentRepository; - _mediaRepository = mediaRepository; - _memberRepository = memberRepository; - GetXmlDocument = getXmlDocument ?? throw new ArgumentNullException(nameof(getXmlDocument)); - _xmlFileEnabled = false; - _xmlFileName = TestHelper.IOHelper.MapPath(SystemFiles.GetContentCacheXml(hostingEnvironment)); - // do not plug events, we may not have what it takes to handle them - } - - private void InitializeFilePersister(MainDom mainDom) - { - if (SyncToXmlFile == false) return; - - var loggerFactory = Current.LoggerFactory; - - // there's always be one task keeping a ref to the runner - // so it's safe to just create it as a local var here - var runner = new BackgroundTaskRunner(new BackgroundTaskRunnerOptions - { - LongRunning = true, - KeepAlive = true, - Hosted = false // main domain will take care of stopping the runner (see below) - }, loggerFactory.CreateLogger>(), _hostingLifetime); - - // create (and add to runner) - _persisterTask = new XmlStoreFilePersister(runner, this, loggerFactory.CreateLogger()); - - var registered = mainDom.Register( - null, - () => - { - // once released, the cache still works but does not write to file anymore, - // which is OK with database server messenger but will cause data loss with - // another messenger... - - runner.Shutdown(false, true); // wait until flushed - _persisterTask = null; // fail fast - _released = true; - }); - - // failed to become the main domain, we will never use the file - if (registered == false) - runner.Shutdown(false, true); - - _released = registered == false; - } - - private void Initialize(bool testing, bool enableRepositoryEvents) - { - - // not so soon! if eg installing we may not be able to load content yet - // so replace this by LazyInitializeContent() called in Xml ppty getter - //InitializeContent(); - } - - private void LazyInitializeContent() - { - if (_xml != null) return; - - // and populate the cache - using (var safeXml = GetSafeXmlWriter()) - { - if (_xml != null) return; // double-check - - // if we don't use the file then LoadXmlLocked will not even - // read from the file and will go straight to database - LoadXmlLocked(safeXml, out bool registerXmlChange); - - // if we use the file and registerXmlChange is true this will - // write to file, else it will not - safeXml.AcceptChanges(registerXmlChange); - } - } - - public void Dispose() - { - } - - #endregion - - #region Configuration - - // gathering configuration options here to document what they mean - - private readonly bool _xmlFileEnabled = true; - - // whether the disk cache is enabled - private bool XmlFileEnabled => true; - - // whether the disk cache is enabled and to update the disk cache when xml changes - private bool SyncToXmlFile => true; - - // whether the disk cache is enabled and to reload from disk cache if it changes - private bool SyncFromXmlFile => false; - - // whether _xml is immutable or not (achieved by cloning before changing anything) - private static bool XmlIsImmutable => true; - - // whether to keep version of everything (incl. medias & members) in cmsPreviewXml - // for audit purposes - false by default, not in umbracoSettings.config - // whether to... no idea what that one does - // it is false by default and not in UmbracoSettings.config anymore - ignoring - /* - private static bool GlobalPreviewStorageEnabled - { - get { return UmbracoConfig.For.UmbracoSettings().Content.GlobalPreviewStorageEnabled; } - } - */ - - // ensures config is valid - private void EnsureConfigurationIsValid() - { - if (SyncToXmlFile && SyncFromXmlFile) - throw new Exception("Cannot run with both ContinouslyUpdateXmlDiskCache and XmlContentCheckForDiskChanges being true."); - - if (XmlIsImmutable == false) - //Current.Logger.LogWarning("Running with CloneXmlContent being false is a bad idea."); - Current.Logger.LogWarning("CloneXmlContent is false - ignored, we always clone."); - - // note: if SyncFromXmlFile then we should also disable / warn that local edits are going to cause issues... - } - - #endregion - - #region Xml - - /// - /// Gets or sets the delegate used to retrieve the Xml content, used for unit tests, else should - /// be null and then the default content will be used. For non-preview content only. - /// - /// - /// The default content ONLY works when in the context an Http Request mostly because the - /// 'content' object heavily relies on HttpContext, SQL connections and a bunch of other stuff - /// that when run inside of a unit test fails. - /// - public Func GetXmlDocument { get; set; } - - private XmlDocument _xmlDocument; // supplied xml document (for tests) - private volatile XmlDocument _xml; // master xml document - private readonly SystemLock _xmlLock = new SystemLock(); // protects _xml - - // to be used by PublishedContentCache only - // for non-preview content only - public XmlDocument Xml - { - get - { - if (_xml != null) - return _xml; - - if (_xmlDocument != null) - { - _xml = _xmlDocument; - _xmlDocument = null; - return _xml; - } - - if (GetXmlDocument != null) - return _xml = GetXmlDocument(); - - LazyInitializeContent(); - ReloadXmlFromFileIfChanged(); - return _xml; - } - } - - // Gets the temp. Xml managed by SafeXmlReaderWrite, if any - public XmlDocument TempXml => SafeXmlReaderWriter.Get(_scopeProvider)?.Xml; - - // assumes xml lock - private void SetXmlLocked(XmlDocument xml, bool registerXmlChange) - { - // this is the ONLY place where we write to _xml - _xml = xml; - - _routesCache?.Clear(); // anytime we set _xml - - if (registerXmlChange == false || SyncToXmlFile == false) - return; - - _persisterTask = _persisterTask?.Touch(); - } - - private static XmlDocument EnsureSchema(string contentTypeAlias, XmlDocument xml) - { - string subset = null; - - // get current doctype - var n = xml.FirstChild; - while (n.NodeType != XmlNodeType.DocumentType && n.NextSibling != null) - n = n.NextSibling; - if (n.NodeType == XmlNodeType.DocumentType) - subset = ((XmlDocumentType)n).InternalSubset; - - // ensure it contains the content type - if (subset != null && subset.Contains($"")) - return xml; - - // alas, that does not work, replacing a doctype is ignored and GetElementById fails - // - //// remove current doctype, set new doctype - //xml.RemoveChild(n); - //subset = string.Format("{0}{0}{2}", Environment.NewLine, contentTypeAlias, subset); - //var doctype = xml.CreateDocumentType("root", null, null, subset); - //xml.InsertAfter(doctype, xml.FirstChild); - - var xml2 = new XmlDocument(); - subset = string.Format("{0}{0}{2}", Environment.NewLine, contentTypeAlias, subset); - var doctype = xml2.CreateDocumentType("root", null, null, subset); - xml2.AppendChild(doctype); - xml2.AppendChild(xml2.ImportNode(xml.DocumentElement, true)); - return xml2; - } - - private static void InitializeXml(XmlDocument xml, string dtd) - { - // prime the xml document with an inline dtd and a root element - xml.LoadXml(string.Format("{0}{1}{0}", Environment.NewLine, dtd)); - } - - /// - /// Generates the complete (simplified) XML DTD. - /// - /// The DTD as a string - private string GetDtd() - { - var dtd = new StringBuilder(); - dtd.AppendLine(" x.Alias.ToSafeAlias(_shortStringHelper)).WhereNotNull(); - foreach (var alias in aliases) - { - dtdInner.AppendLine($""); - dtdInner.AppendLine($""); - } - dtd.Append(dtdInner); - } - catch (Exception ex) - { - Current.Logger.LogError(ex, "Failed to build a DTD for the Xml cache."); - } - - dtd.AppendLine("]>"); - return dtd.ToString(); - } - - // try to load from file, otherwise database - // assumes xml lock (file is always locked) - private void LoadXmlLocked(SafeXmlReaderWriter safeXml, out bool registerXmlChange) - { - Current.Logger.LogDebug("Loading Xml..."); - - // try to get it from the file - if (XmlFileEnabled && (safeXml.Xml = LoadXmlFromFile()) != null) - { - registerXmlChange = false; // loaded from disk, do NOT write back to disk! - return; - } - - // get it from the database, and register - LoadXmlTreeFromDatabaseLocked(safeXml); - registerXmlChange = true; - } - - public XmlNode GetMediaXmlNode(int mediaId) - { - // there's only one version for medias - - const string sql = @"SELECT umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, umbracoNode.Level, -cmsContentXml.xml, 1 AS published -FROM umbracoNode -JOIN cmsContentXml ON (cmsContentXml.nodeId=umbracoNode.id) -WHERE umbracoNode.nodeObjectType = @nodeObjectType -AND (umbracoNode.id=@id)"; - - XmlDto xmlDto; - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.MediaTree); - var xmlDtos = scope.Database.Query(sql, - new - { - nodeObjectType = Constants.ObjectTypes.Media, - id = mediaId - }); - xmlDto = xmlDtos.FirstOrDefault(); - scope.Complete(); - } - - if (xmlDto == null) return null; - - var doc = new XmlDocument(); - var xml = doc.ReadNode(XmlReader.Create(new StringReader(xmlDto.Xml))); - return xml; - } - - public XmlNode GetMemberXmlNode(int memberId) - { - // there's only one version for members - - const string sql = @"SELECT umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, umbracoNode.Level, -cmsContentXml.xml, 1 AS published -FROM umbracoNode -JOIN cmsContentXml ON (cmsContentXml.nodeId=umbracoNode.id) -WHERE umbracoNode.nodeObjectType = @nodeObjectType -AND (umbracoNode.id=@id)"; - - XmlDto xmlDto; - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.MemberTree); - var xmlDtos = scope.Database.Query(sql, - new - { - nodeObjectType = Constants.ObjectTypes.Member, - id = memberId - }); - xmlDto = xmlDtos.FirstOrDefault(); - scope.Complete(); - } - - if (xmlDto == null) return null; - - var doc = new XmlDocument(); - var xml = doc.ReadNode(XmlReader.Create(new StringReader(xmlDto.Xml))); - return xml; - } - - private static readonly string PreviewXmlNodeSql = $@"SELECT umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, umbracoNode.Level, -cmsPreviewXml.xml, {Constants.DatabaseSchema.Tables.Document}.published -FROM umbracoNode -JOIN cmsPreviewXml ON (cmsPreviewXml.nodeId=umbracoNode.id) -JOIN {Constants.DatabaseSchema.Tables.Document} ON ({Constants.DatabaseSchema.Tables.Document}.nodeId=umbracoNode.id) -WHERE umbracoNode.nodeObjectType = @nodeObjectType -AND (umbracoNode.id=@id)"; - - public XmlNode GetPreviewXmlNode(int contentId) - { - var sql = PreviewXmlNodeSql; - - XmlDto xmlDto; - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.ContentTree); - var xmlDtos = scope.Database.Query(sql, - new - { - nodeObjectType = Constants.ObjectTypes.Document, - id = contentId - }); - xmlDto = xmlDtos.FirstOrDefault(); - scope.Complete(); - } - if (xmlDto == null) return null; - - var doc = new XmlDocument(); - var xml = doc.ReadNode(XmlReader.Create(new StringReader(xmlDto.Xml))); - if (xml?.Attributes == null) return null; - - if (xmlDto.Published == false) - xml.Attributes.Append(doc.CreateAttribute("isDraft")); - return xml; - } - - public XmlDocument GetMediaXml() - { - // this is not efficient at all, not cached, nothing - // just here to replicate what uQuery was doing and show it can be done - // but really - should not be used - - return LoadMoreXmlFromDatabase(Constants.ObjectTypes.Media); - } - - public XmlDocument GetMemberXml() - { - // this is not efficient at all, not cached, nothing - // just here to replicate what uQuery was doing and show it can be done - // but really - should not be used - - return LoadMoreXmlFromDatabase(Constants.ObjectTypes.Member); - } - - public XmlDocument GetPreviewXml(int contentId, bool includeSubs) - { - var content = _contentService.GetById(contentId); - - var doc = (XmlDocument)Xml.Clone(); - if (content == null) return doc; - - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.ContentTree); - var sqlSyntax = scope.SqlContext.SqlSyntax; - - var sql = ReadCmsPreviewXmlSql1; - sql += " @path LIKE " + sqlSyntax.GetConcat("umbracoNode.Path", "',%"); // concat(umbracoNode.path, ',%') - if (includeSubs) sql += " OR umbracoNode.path LIKE " + sqlSyntax.GetConcat("@path", "',%"); // concat(@path, ',%') - sql += ReadCmsPreviewXmlSql2; - - var xmlDtos = scope.Database.Query(sql, - new - { - nodeObjectType = Constants.ObjectTypes.Document, - path = content.Path, - }); - - foreach (var xmlDto in xmlDtos) - { - var xml = xmlDto.XmlNode = doc.ReadNode(XmlReader.Create(new StringReader(xmlDto.Xml))); - if (xml?.Attributes == null) continue; - if (xmlDto.Published == false) - xml.Attributes.Append(doc.CreateAttribute("isDraft")); - doc = AddOrUpdateXmlNode(doc, xmlDto); - } - - scope.Complete(); - } - - return doc; - } - - // NOTE - // - this is NOT a reader/writer lock and each lock is exclusive - // - these locks are NOT reentrant / recursive - // - // should we have async versions that would do: ? - // var releaser = await _xmlLock.LockAsync(); - // - // TODO: not sure about the "resync current published snapshot" thing here, see 7.6... - - // gets a locked safe read access to the main xml - private SafeXmlReaderWriter GetSafeXmlReader() - { - return SafeXmlReaderWriter.Get(_scopeProvider, _xmlLock, _xml, - ResyncCurrentPublishedSnapshot, - (xml, registerXmlChange) => - { - SetXmlLocked(xml, registerXmlChange); - ResyncCurrentPublishedSnapshot(xml); - }, false); - } - - // gets a locked safe write access to the main xml (cloned) - private SafeXmlReaderWriter GetSafeXmlWriter() - { - return SafeXmlReaderWriter.Get(_scopeProvider, _xmlLock, _xml, - ResyncCurrentPublishedSnapshot, - (xml, registerXmlChange) => - { - SetXmlLocked(xml, registerXmlChange); - ResyncCurrentPublishedSnapshot(xml); - }, true); - } - - private const string ChildNodesXPath = "./* [@id]"; - private const string DataNodesXPath = "./* [not(@id)]"; - - #endregion - - #region File - - private readonly string _xmlFileName; - private DateTime _lastFileRead; // last time the file was read - private DateTime _nextFileCheck; // last time we checked whether the file was changed - - public void EnsureFilePermission() - { - // TODO: but do we really have a store, initialized, at that point? - var filename = _xmlFileName + ".temp"; - File.WriteAllText(filename, "TEMP"); - File.Delete(filename); - } - - // not used - just try to read the file - //private bool XmlFileExists - //{ - // get - // { - // // check that the file exists and has content (is not empty) - // var fileInfo = new FileInfo(_xmlFileName); - // return fileInfo.Exists && fileInfo.Length > 0; - // } - //} - - private DateTime XmlFileLastWriteTime - { - get - { - var fileInfo = new FileInfo(_xmlFileName); - return fileInfo.Exists ? fileInfo.LastWriteTimeUtc : DateTime.MinValue; - } - } - - // invoked by XmlStoreFilePersister ONLY and that one manages the MainDom, ie it - // will NOT try to save once the current app domain is not the main domain anymore - // (no need to test _released) - internal void SaveXmlToFile() - { - Current.Logger.LogInformation("Save Xml to file..."); - - try - { - var xml = _xml; // capture (atomic + volatile), immutable anyway - if (xml == null) return; - - // delete existing file, if any - DeleteXmlFile(); - - // ensure cache directory exists - var directoryName = Path.GetDirectoryName(_xmlFileName); - if (directoryName == null) - throw new Exception($"Invalid XmlFileName \"{_xmlFileName}\"."); - if (File.Exists(_xmlFileName) == false && Directory.Exists(directoryName) == false) - Directory.CreateDirectory(directoryName); - - // save - using (var fs = new FileStream(_xmlFileName, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 4096, useAsync: true)) - { - var bytes = Encoding.UTF8.GetBytes(SaveXmlToString(xml)); - fs.Write(bytes, 0, bytes.Length); - } - - Current.Logger.LogInformation("Saved Xml to file."); - } - catch (Exception ex) - { - // if something goes wrong remove the file - DeleteXmlFile(); - - Current.Logger.LogError(ex, "Failed to save Xml to file '{FileName}'.", _xmlFileName); - } - } - - // invoked by XmlStoreFilePersister ONLY and that one manages the MainDom, ie it - // will NOT try to save once the current app domain is not the main domain anymore - // (no need to test _released) - internal async Task SaveXmlToFileAsync() - { - Current.Logger.LogInformation("Save Xml to file..."); - - try - { - var xml = _xml; // capture (atomic + volatile), immutable anyway - if (xml == null) return; - - // delete existing file, if any - DeleteXmlFile(); - - // ensure cache directory exists - var directoryName = Path.GetDirectoryName(_xmlFileName); - if (directoryName == null) - throw new Exception($"Invalid XmlFileName \"{_xmlFileName}\"."); - if (File.Exists(_xmlFileName) == false && Directory.Exists(directoryName) == false) - Directory.CreateDirectory(directoryName); - - // save - using (var fs = new FileStream(_xmlFileName, FileMode.Create, FileAccess.Write, FileShare.Read, bufferSize: 4096, useAsync: true)) - { - var bytes = Encoding.UTF8.GetBytes(SaveXmlToString(xml)); - await fs.WriteAsync(bytes, 0, bytes.Length); - } - - Current.Logger.LogInformation("Saved Xml to file."); - } - catch (Exception ex) - { - // if something goes wrong remove the file - DeleteXmlFile(); - - Current.Logger.LogError(ex, "Failed to save Xml to file '{FileName}'.", _xmlFileName); - } - } - - private static string SaveXmlToString(XmlDocument xml) - { - // using that one method because we want to have proper indent - // and in addition, writing async is never fully async because - // although the writer is async, xml.WriteTo() will not async - - // that one almost works but... "The elements are indented as long as the element - // does not contain mixed content. Once the WriteString or WriteWhitespace method - // is called to write out a mixed element content, the XmlWriter stops indenting. - // The indenting resumes once the mixed content element is closed." - says MSDN - // about XmlWriterSettings.Indent - - // so ImportContentBase must also make sure of ignoring whitespaces! - - var sb = new StringBuilder(); - using (var xmlWriter = XmlWriter.Create(sb, new XmlWriterSettings - { - Indent = true, - Encoding = Encoding.UTF8, - //OmitXmlDeclaration = true - })) - { - //xmlWriter.WriteProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\""); - xml.WriteTo(xmlWriter); // already contains the xml declaration - } - return sb.ToString(); - } - - private XmlDocument LoadXmlFromFile() - { - // do NOT try to load if we are not the main domain anymore - if (_released) return null; - - Current.Logger.LogInformation("Load Xml from file..."); - - try - { - var xml = new XmlDocument(); - using (var fs = new FileStream(_xmlFileName, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - xml.Load(fs); - } - _lastFileRead = DateTime.UtcNow; - Current.Logger.LogInformation("Loaded Xml from file."); - return xml; - } - catch (FileNotFoundException) - { - Current.Logger.LogWarning("Failed to load Xml, file does not exist."); - return null; - } - catch (Exception ex) - { - Current.Logger.LogError(ex, "Failed to load Xml from file '{FileName}'.", _xmlFileName); - try - { - DeleteXmlFile(); - } - catch - { - // don't make it worse: could be that we failed to read because we cannot - // access the file, in which case we won't be able to delete it either - } - return null; - } - } - - private void DeleteXmlFile() - { - if (File.Exists(_xmlFileName) == false) return; - File.SetAttributes(_xmlFileName, FileAttributes.Normal); - File.Delete(_xmlFileName); - } - - private void ReloadXmlFromFileIfChanged() - { - if (SyncFromXmlFile == false) return; - - var now = DateTime.UtcNow; - if (now < _nextFileCheck) return; - - // time to check - _nextFileCheck = now.AddSeconds(1); // check every 1s - if (XmlFileLastWriteTime <= _lastFileRead) return; - - Current.Logger.LogDebug("Xml file change detected, reloading."); - - // time to read - - using (var safeXml = GetSafeXmlWriter()) - { - LoadXmlLocked(safeXml, out bool registerXmlChange); // updates _lastFileRead - safeXml.AcceptChanges(registerXmlChange); - } - } - - #endregion - - #region Database - - private static readonly string ReadTreeCmsContentXmlSql = $@"SELECT - umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, umbracoNode.level, umbracoNode.path, - cmsContentXml.xml, cmsContentXml.rv, {Constants.DatabaseSchema.Tables.Document}.published -FROM umbracoNode -JOIN cmsContentXml ON (cmsContentXml.nodeId=umbracoNode.id) -JOIN {Constants.DatabaseSchema.Tables.Document} ON ({Constants.DatabaseSchema.Tables.Document}.nodeId=umbracoNode.id) -WHERE umbracoNode.nodeObjectType = @nodeObjectType AND {Constants.DatabaseSchema.Tables.Document}.published=1 -ORDER BY umbracoNode.level, umbracoNode.sortOrder"; - - private static readonly string ReadBranchCmsContentXmlSql = $@"SELECT - umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, umbracoNode.level, umbracoNode.path, - cmsContentXml.xml, cmsContentXml.rv, {Constants.DatabaseSchema.Tables.Document}.published -FROM umbracoNode -JOIN cmsContentXml ON (cmsContentXml.nodeId=umbracoNode.id) -JOIN {Constants.DatabaseSchema.Tables.Document} ON ({Constants.DatabaseSchema.Tables.Document}.nodeId=umbracoNode.id) -WHERE umbracoNode.nodeObjectType = @nodeObjectType AND {Constants.DatabaseSchema.Tables.Document}.published=1 AND (umbracoNode.id = @id OR umbracoNode.path LIKE @path) -ORDER BY umbracoNode.level, umbracoNode.sortOrder"; - - private static readonly string ReadCmsContentXmlForContentTypesSql = $@"SELECT - umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, umbracoNode.level, umbracoNode.path, - cmsContentXml.xml, cmsContentXml.rv, {Constants.DatabaseSchema.Tables.Document}.published -FROM umbracoNode -JOIN cmsContentXml ON (cmsContentXml.nodeId=umbracoNode.id) -JOIN {Constants.DatabaseSchema.Tables.Document} ON ({Constants.DatabaseSchema.Tables.Document}.nodeId=umbracoNode.id) -JOIN {Constants.DatabaseSchema.Tables.Content} ON ({Constants.DatabaseSchema.Tables.Document}.nodeId={Constants.DatabaseSchema.Tables.Content}.nodeId) -WHERE umbracoNode.nodeObjectType = @nodeObjectType AND {Constants.DatabaseSchema.Tables.Document}.published=1 AND {Constants.DatabaseSchema.Tables.Content}.contentTypeId IN (@ids) -ORDER BY umbracoNode.level, umbracoNode.sortOrder"; - - private const string ReadMoreCmsContentXmlSql = @"SELECT - umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, umbracoNode.level, umbracoNode.path, - cmsContentXml.xml, cmsContentXml.rv, 1 AS published -FROM umbracoNode -JOIN cmsContentXml ON (cmsContentXml.nodeId=umbracoNode.id) -WHERE umbracoNode.nodeObjectType = @nodeObjectType -ORDER BY umbracoNode.level, umbracoNode.sortOrder"; - - private static readonly string ReadCmsPreviewXmlSql1 = $@"SELECT - umbracoNode.id, umbracoNode.parentId, umbracoNode.sortOrder, umbracoNode.level, umbracoNode.path, - cmsPreviewXml.xml, cmsPreviewXml.rv, {Constants.DatabaseSchema.Tables.Document}.published -FROM umbracoNode -JOIN cmsPreviewXml ON (cmsPreviewXml.nodeId=umbracoNode.id) -JOIN {Constants.DatabaseSchema.Tables.Document} ON ({Constants.DatabaseSchema.Tables.Document}.nodeId=umbracoNode.id) -WHERE umbracoNode.nodeObjectType = @nodeObjectType AND {Constants.DatabaseSchema.Tables.Document}.published=1 -AND (umbracoNode.path=@path OR"; // @path LIKE concat(umbracoNode.path, ',%')"; - - private const string ReadCmsPreviewXmlSql2 = @") -ORDER BY umbracoNode.level, umbracoNode.sortOrder"; - - // ReSharper disable once ClassNeverInstantiated.Local - private class XmlDto - { - // ReSharper disable UnusedAutoPropertyAccessor.Local - - public int Id { get; set; } - public long Rv { get; set; } - public int ParentId { get; set; } - //public int SortOrder { get; set; } - public int Level { get; set; } - public string Path { get; set; } - public string Xml { get; set; } - public bool Published { get; set; } - - [Ignore] - public XmlNode XmlNode { get; set; } - - // ReSharper restore UnusedAutoPropertyAccessor.Local - } - - // assumes xml lock - private void LoadXmlTreeFromDatabaseLocked(SafeXmlReaderWriter safeXml) - { - // initialize the document ready for the composition of content - var xml = new XmlDocument(); - InitializeXml(xml, GetDtd()); - - XmlNode parent = null; - var parentId = 0; - - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.ContentTree); - - // get xml - var xmlDtos = scope.Database.Query(ReadTreeCmsContentXmlSql, - new { nodeObjectType = Constants.ObjectTypes.Document }); - - foreach (var xmlDto in xmlDtos) - { - xmlDto.XmlNode = ImportContent(xml, xmlDto); // parse into a DOM node - - if (parent == null || parentId != xmlDto.ParentId) - { - parent = xmlDto.ParentId == -1 - ? xml.DocumentElement - : xml.GetElementById(xmlDto.ParentId.ToInvariantString()); - - if (parent == null) continue; - - parentId = xmlDto.ParentId; - } - - parent.AppendChild(xmlDto.XmlNode); - } - - scope.Complete(); - } - - safeXml.Xml = xml; - } - - private XmlDocument LoadMoreXmlFromDatabase(Guid nodeObjectType) - { - var xmlDoc = new XmlDocument(); - - using (var scope = _scopeProvider.CreateScope()) - { - if (nodeObjectType == Constants.ObjectTypes.Document) - scope.ReadLock(Constants.Locks.ContentTree); - else if (nodeObjectType == Constants.ObjectTypes.Media) - scope.ReadLock(Constants.Locks.MediaTree); - else if (nodeObjectType == Constants.ObjectTypes.Member) - scope.ReadLock(Constants.Locks.MemberTree); - - var xmlDtos = scope.Database.Query(ReadMoreCmsContentXmlSql, - new { /*@nodeObjectType =*/ nodeObjectType }); - - // Initialize the document ready for the final composition of content - InitializeXml(xmlDoc, string.Empty); - - XmlNode parent = null; - var parentId = 0; - - foreach (var xmlDto in xmlDtos) - { - // and parse it into a DOM node - var node = xmlDoc.ReadNode(XmlReader.Create(new StringReader(xmlDto.Xml), new XmlReaderSettings - { - IgnoreWhitespace = true - })); - - if (parent == null || parentId != xmlDto.ParentId) - { - parent = xmlDto.ParentId == -1 - ? xmlDoc.DocumentElement - : xmlDoc.GetElementById(xmlDto.ParentId.ToInvariantString()); - - if (parent == null) - continue; - - parentId = xmlDto.ParentId; - } - - parent.AppendChild(node); - } - - scope.Complete(); - } - - return xmlDoc; - } - - // internal - used by umbraco.content.RefreshContentFromDatabase[Async] - internal void ReloadXmlFromDatabase() - { - // event - cancel - - // nobody should work on the Xml while we load - using (var safeXml = GetSafeXmlWriter()) - { - LoadXmlTreeFromDatabaseLocked(safeXml); - safeXml.AcceptChanges(); - } - } - - #endregion - - #region Handle Distributed Notifications for Memory Xml - - // NOT using events, see notes in IPublishedCachesService - - public void Notify(ContentCacheRefresher.JsonPayload[] payloads, out bool draftChanged, out bool publishedChanged) - { - draftChanged = publishedChanged = false; - if (_xml == null) return; // not initialized yet! - - draftChanged = true; // by default - we don't track drafts - publishedChanged = false; - - // process all changes on one xml clone - using (var safeXml = GetSafeXmlWriter()) - { - foreach (var payload in payloads) - { - Current.Logger.LogDebug("Notified {ChangeTypes} for content {ContentId}", payload.ChangeTypes, payload.Id); - - if (payload.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) - { - LoadXmlTreeFromDatabaseLocked(safeXml); - publishedChanged = true; - continue; - } - - if (payload.ChangeTypes.HasType(TreeChangeTypes.Remove)) - { - var toRemove = safeXml.Xml.GetElementById(payload.Id.ToInvariantString()); - if (toRemove != null) - { - if (toRemove.ParentNode == null) throw new Exception("oops"); - toRemove.ParentNode.RemoveChild(toRemove); - publishedChanged = true; - } - continue; - } - - if (payload.ChangeTypes.HasTypesNone(TreeChangeTypes.RefreshNode | TreeChangeTypes.RefreshBranch)) - { - // ?! - continue; - } - - var content = _contentService.GetById(payload.Id); - var current = safeXml.Xml.GetElementById(payload.Id.ToInvariantString()); - - if (content == null || content.Published == false || content.Trashed) - { - // no published version - Current.Logger.LogDebug("Notified, content {ContentId} has no published version.", payload.Id); - - if (current != null) - { - // remove from xml if exists - if (current.ParentNode == null) throw new Exception("oops"); - current.ParentNode.RemoveChild(current); - publishedChanged = true; - } - - continue; - } - - // else we have a published version - - // get xml - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.ContentTree); - - // that query is yielding results so will only load what's needed - var xmlDtos = scope.Database.Query(ReadBranchCmsContentXmlSql, - new - { - nodeObjectType = Constants.ObjectTypes.Document, - path = content.Path + ",%", - id = content.Id - }); - - // 'using' the enumerator ensures that the enumeration is properly terminated even if abandoned - // otherwise, it would leak an open reader & an un-released database connection - // see PetaPoco.Query(Type[] types, Delegate cb, string sql, params object[] args) - // and read http://blogs.msdn.com/b/oldnewthing/archive/2008/08/14/8862242.aspx - // - using (var dtos = xmlDtos.GetEnumerator()) - { - if (dtos.MoveNext() == false) - { - // gone fishing, remove (possible race condition) - Current.Logger.LogDebug("Notified, content {ContentId} gone fishing.", payload.Id); - - if (current != null) - { - // remove from xml if exists - if (current.ParentNode == null) throw new Exception("oops"); - current.ParentNode.RemoveChild(current); - publishedChanged = true; - } - continue; - } - - if (dtos.Current.Id != content.Id) - throw new Exception("oops"); // first one should be 'current' - var currentDto = dtos.Current; - - // note: if anything eg parentId or path or level has changed, then rv has changed too - var currentRv = current == null ? -1 : int.Parse(current.Attributes["rv"].Value); - - // if exists and unchanged and not refreshing the branch, skip entirely - if (current != null - && currentRv == currentDto.Rv - && payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch) == false) - continue; - - currentDto.XmlNode = ImportContent(safeXml.Xml, currentDto); - - // note: Examine would not be able to do the path trick below, and we cannot help for - // unpublished content, so it *is* possible that Examine is inconsistent for a while, - // though events should get it consistent eventually. - - // note: if path has changed we must do a branch refresh, even if the event is not requiring - // it, otherwise we would update the local node and not its children, who would then have - // inconsistent level (and path) attributes. - - var refreshBranch = current == null - || payload.ChangeTypes.HasType(TreeChangeTypes.RefreshBranch) - || current.Attributes["path"].Value != currentDto.Path; - - if (refreshBranch) - { - // remove node if exists - if (current != null) - { - if (current.ParentNode == null) throw new Exception("oops"); - current.ParentNode.RemoveChild(current); - } - - // insert node - var newParent = currentDto.ParentId == -1 - ? safeXml.Xml.DocumentElement - : safeXml.Xml.GetElementById(currentDto.ParentId.ToInvariantString()); - if (newParent == null) continue; - newParent.AppendChild(currentDto.XmlNode); - XmlHelper.SortNode(newParent, ChildNodesXPath, currentDto.XmlNode, - x => x.AttributeValue("sortOrder")); - - // add branch (don't try to be clever) - while (dtos.MoveNext()) - { - // dtos are ordered by sortOrder already - var dto = dtos.Current; - - // if node is already there, somewhere, remove - var n = safeXml.Xml.GetElementById(dto.Id.ToInvariantString()); - if (n != null) - { - if (n.ParentNode == null) throw new Exception("oops"); - n.ParentNode.RemoveChild(n); - } - - // find parent, add node - var p = safeXml.Xml.GetElementById(dto.ParentId.ToInvariantString()); // branch, so parentId > 0 - // takes care of out-of-sync & masked - p?.AppendChild(dto.XmlNode); - } - } - else - { - // in-place - safeXml.Xml = AddOrUpdateXmlNode(safeXml.Xml, currentDto); - } - } - - scope.Complete(); - } - - publishedChanged = true; - } - - if (publishedChanged) - safeXml.AcceptChanges(); - } - } - - public void Notify(ContentTypeCacheRefresher.JsonPayload[] payloads) - { - if (_xml == null) return; // not initialized yet! - - // see ContentTypeServiceBase - // in all cases we just want to clear the content type cache - // the type will be reloaded if/when needed - foreach (var payload in payloads) - _contentTypeCache.ClearContentType(payload.Id); - - // process content types / content cache - // only those that have been changed - with impact on content - RefreshMain - // for those that have been removed, content is removed already - var ids = payloads - .Where(x => x.ItemType == typeof(IContentType).Name && x.ChangeTypes.HasType(ContentTypeChangeTypes.RefreshMain)) - .Select(x => x.Id) - .ToArray(); - - foreach (var payload in payloads) - Current.Logger.LogDebug("Notified {ChangeTypes} for content type {ContentTypeId}", payload.ChangeTypes, payload.Id); - - if (ids.Length > 0) // must have refreshes, not only removes - RefreshContentTypes(ids); - - // ignore media and member types - we're not caching them - } - - public void Notify(DataTypeCacheRefresher.JsonPayload[] payloads) - { - if (_xml == null) return; // not initialized yet! - - // see above - // in all cases we just want to clear the content type cache - // the types will be reloaded if/when needed - foreach (var payload in payloads) - _contentTypeCache.ClearDataType(payload.Id); - - foreach (var payload in payloads) - Current.Logger.LogDebug("Notified {RemovedStatus} for data type {payload.Id}", - payload.Removed ? "Removed" : "Refreshed", - payload.Id); - - // that's all we need to do as the changes have NO impact whatsoever on the Xml content - - // ignore media and member types - we're not caching them - } - - private void ResyncCurrentPublishedSnapshot(XmlDocument xml) - { - var publishedSnapshot = (PublishedSnapshot) _publishedSnapshotAccessor.PublishedSnapshot; - if (publishedSnapshot == null) return; - ((PublishedContentCache) publishedSnapshot.Content).Resync(xml); - ((PublishedMediaCache) publishedSnapshot.Media).Resync(); - - // not trying to resync members or domains, which are not cached really - } - - #endregion - - #region Manage change - - private void RefreshContentTypes(IEnumerable ids) - { - using (var safeXml = GetSafeXmlWriter()) - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.ContentTree); - var xmlDtos = scope.Database.Query(ReadCmsContentXmlForContentTypesSql, - new { nodeObjectType = Constants.ObjectTypes.Document, /*@ids =*/ ids }); - - foreach (var xmlDto in xmlDtos) - { - xmlDto.XmlNode = safeXml.Xml.ReadNode(XmlReader.Create(new StringReader(xmlDto.Xml))); - safeXml.Xml = AddOrUpdateXmlNode(safeXml.Xml, xmlDto); - } - - scope.Complete(); - safeXml.AcceptChanges(); - } - } - - // nothing to do, we have no cache - //private void RefreshMediaTypes(IEnumerable ids) - //{ } - - // nothing to do, we have no cache - //private void RefreshMemberTypes(IEnumerable ids) - //{ } - - // adds or updates a node (docNode) into a cache (xml) - private static XmlDocument AddOrUpdateXmlNode(XmlDocument xml, XmlDto xmlDto) - { - // sanity checks - var docNode = xmlDto.XmlNode; - if (xmlDto.Id != docNode.AttributeValue("id")) - throw new ArgumentException("Values of id and docNode/@id are different."); - if (xmlDto.ParentId != docNode.AttributeValue("parentID")) - throw new ArgumentException("Values of parentId and docNode/@parentID are different."); - - // find the document in the cache - XmlNode currentNode = xml.GetElementById(xmlDto.Id.ToInvariantString()); - - // if the document is not there already then it's a new document - // we must make sure that its document type exists in the schema - if (currentNode == null) - { - var xml2 = EnsureSchema(docNode.Name, xml); - if (ReferenceEquals(xml, xml2) == false) - docNode = xml2.ImportNode(docNode, true); - xml = xml2; - } - - // find the parent - XmlNode parentNode = xmlDto.Level == 1 - ? xml.DocumentElement - : xml.GetElementById(xmlDto.ParentId.ToInvariantString()); - - // no parent = cannot do anything - if (parentNode == null) - return xml; - - // insert/move the node under the parent - if (currentNode == null) - { - // document not there, new node, append - currentNode = docNode; - parentNode.AppendChild(currentNode); - } - else - { - // document found... we could just copy the currentNode children nodes over under - // docNode, then remove currentNode and insert docNode... the code below tries to - // be clever and faster, though only benchmarking could tell whether it's worth the - // pain... - - // first copy current parent ID - so we can compare with target parent - var moving = currentNode.AttributeValue("parentID") != xmlDto.ParentId; - - if (docNode.Name == currentNode.Name) - { - // name has not changed, safe to just update the current node - // by transferring values eg copying the attributes, and importing the data elements - TransferValuesFromDocumentXmlToPublishedXml(docNode, currentNode); - - // if moving, move the node to the new parent - // else it's already under the right parent - // (but maybe the sort order has been updated) - if (moving) - parentNode.AppendChild(currentNode); // remove then append to parentNode - } - else - { - // name has changed, must use docNode (with new name) - // move children nodes from currentNode to docNode (already has properties) - var children = currentNode.SelectNodes(ChildNodesXPath); - if (children == null) throw new Exception("oops"); - foreach (XmlNode child in children) - docNode.AppendChild(child); // remove then append to docNode - - // and put docNode in the right place - if parent has not changed, then - // just replace, else remove currentNode and insert docNode under the right parent - // (but maybe not at the right position due to sort order) - if (moving) - { - if (currentNode.ParentNode == null) throw new Exception("oops"); - currentNode.ParentNode.RemoveChild(currentNode); - parentNode.AppendChild(docNode); - } - else - { - // replacing might screw the sort order - parentNode.ReplaceChild(docNode, currentNode); - } - - currentNode = docNode; - } - } - - var attrs = currentNode.Attributes; - if (attrs == null) throw new Exception("oops."); - - var attr = attrs["rv"] ?? attrs.Append(xml.CreateAttribute("rv")); - attr.Value = xmlDto.Rv.ToString(CultureInfo.InvariantCulture); - - attr = attrs["path"] ?? attrs.Append(xml.CreateAttribute("path")); - attr.Value = xmlDto.Path; - - // if the nodes are not ordered, must sort - // (see U4-509 + has to work with ReplaceChild too) - //XmlHelper.SortNodesIfNeeded(parentNode, childNodesXPath, x => x.AttributeValue("sortOrder")); - - // but... - // if we assume that nodes are always correctly sorted - // then we just need to ensure that currentNode is at the right position. - // should be faster that moving all the nodes around. - XmlHelper.SortNode(parentNode, ChildNodesXPath, currentNode, x => x.AttributeValue("sortOrder")); - return xml; - } - - private static void TransferValuesFromDocumentXmlToPublishedXml(XmlNode documentNode, XmlNode publishedNode) - { - // remove all attributes from the published node - if (publishedNode.Attributes == null) throw new Exception("oops"); - publishedNode.Attributes.RemoveAll(); - - // remove all data nodes from the published node - var dataNodes = publishedNode.SelectNodes(DataNodesXPath); - if (dataNodes == null) throw new Exception("oops"); - foreach (XmlNode n in dataNodes) - publishedNode.RemoveChild(n); - - // append all attributes from the document node to the published node - if (documentNode.Attributes == null) throw new Exception("oops"); - foreach (XmlAttribute att in documentNode.Attributes) - ((XmlElement)publishedNode).SetAttribute(att.Name, att.Value); - - // find the first child node, if any - var childNodes = publishedNode.SelectNodes(ChildNodesXPath); - if (childNodes == null) throw new Exception("oops"); - var firstChildNode = childNodes.Count == 0 ? null : childNodes[0]; - - // append all data nodes from the document node to the published node - dataNodes = documentNode.SelectNodes(DataNodesXPath); - if (dataNodes == null) throw new Exception("oops"); - foreach (XmlNode n in dataNodes) - { - if (publishedNode.OwnerDocument == null) throw new Exception("oops"); - var imported = publishedNode.OwnerDocument.ImportNode(n, true); - if (firstChildNode == null) - publishedNode.AppendChild(imported); - else - publishedNode.InsertBefore(imported, firstChildNode); - } - } - - private static XmlNode ImportContent(XmlDocument xml, XmlDto dto) - { - var node = xml.ReadNode(XmlReader.Create(new StringReader(dto.Xml), new XmlReaderSettings - { - IgnoreWhitespace = true - })); - - if (node == null) throw new Exception("oops"); - if (node.Attributes == null) throw new Exception("oops"); - - var attr = xml.CreateAttribute("rv"); - attr.Value = dto.Rv.ToString(CultureInfo.InvariantCulture); - node.Attributes.Append(attr); - - attr = xml.CreateAttribute("path"); - attr.Value = dto.Path; - node.Attributes.Append(attr); - - return node; - } - - #endregion - - #region Handle Repository Events For Database Xml - - // we need them to be "repository" events ie to trigger from within the repository transaction, - // because they need to be consistent with the content that is being refreshed/removed - and that - // should be guaranteed by a DB transaction - // it is not the case at the moment, instead a global lock is used whenever content is modified - well, - // almost: rollback or unpublish do not implement it - nevertheless - - public void Handle(ContentDeletingNotification notification) - { - foreach (IContent entity in notification.DeletedEntities) - { - // We used to do args.Scope.Database, but can't any more because it's not supported by the notification pattern - OnRemovedEntity(null, entity); - } - } - - public void Handle(MediaDeletingNotification notification) - { - foreach (IMedia entity in notification.DeletedEntities) - { - // We used to do args.Scope.Database, but can't any more because it's not supported by the notification pattern - OnRemovedEntity(null, entity); - } - } - - public void Handle(MemberDeletingNotification notification) - { - foreach (IMember entity in notification.DeletedEntities) - { - // We used to do args.Scope.Database, but can't any more because it's not supported by the notification pattern - OnRemovedEntity(null, entity); - } - } - - private static void OnRemovedEntity(IUmbracoDatabase db, IContentBase item) - { - var parms = new { id = item.Id }; - db.Execute("DELETE FROM cmsContentXml WHERE nodeId=@id", parms); - db.Execute("DELETE FROM cmsPreviewXml WHERE nodeId=@id", parms); - - // note: could be optimized by using "WHERE nodeId IN (...)" delete clauses - } - - public void Handle(ContentDeletingVersionsNotification notification) - { - OnRemovedVersion(null, notification.Id, notification.SpecificVersion); - } - - public void Handle(MediaDeletingVersionsNotification notification) - { - OnRemovedVersion(null, notification.Id, notification.SpecificVersion); - } - - private static void OnRemovedVersion(IUmbracoDatabase db, int entityId, int versionId) - { - // we do not version cmsPreviewXml anymore - nothing to do here - } - - private static readonly string[] PropertiesImpactingAllVersions = { "SortOrder", "ParentId", "Level", "Path", "Trashed" }; - - private static bool HasChangesImpactingAllVersions(IContent icontent) - { - var content = (Content)icontent; - - // UpdateDate will be dirty - // Published may be dirty if saving a Published entity - // so cannot do this (would always be true): - //return content.IsEntityDirty(); - - // have to be more precise & specify properties - return PropertiesImpactingAllVersions.Any(content.IsPropertyDirty); - } - - public void Handle(ContentRefreshNotification notification) - { - var db = Mock.Of(); // Notification no longer carries the scope, so we can't get the DB - var entity = notification.Entity; - - // serialize edit values for preview - var editXml = _entitySerializer.Serialize(entity, false).ToDataString(); - - // change below to write only one row - not one per version - var dto1 = new PreviewXmlDto - { - NodeId = entity.Id, - Xml = editXml - }; - OnRepositoryRefreshed(db, dto1); - - // if unpublishing, remove from table - - if (((Content) entity).PublishedState == PublishedState.Unpublishing) - { - db.Execute("DELETE FROM cmsContentXml WHERE nodeId=@id", new { id = entity.Id }); - return; - } - - // need to update the published xml if we're saving the published version, - // or having an impact on that version - we update the published xml even when masked - - // TODO: in the repo... either its 'unpublished' and 'publishing', or 'published' and 'published', this has changed! - // TODO: what are we serializing really? which properties? - - // if not publishing, no change to published xml - if (((Content) entity).PublishedState != PublishedState.Publishing) - return; - - // serialize published values for content cache - var publishedXml = _entitySerializer.Serialize(entity, true).ToDataString(); - var dto2 = new ContentXmlDto { NodeId = entity.Id, Xml = publishedXml }; - OnRepositoryRefreshed(db, dto2); - - } - - public void Handle(MediaRefreshNotification notification) - { - var db = Mock.Of(); // Notification no longer carries the scope, so we can't get the DB - var entity = notification.Entity; - - // for whatever reason we delete some xml when the media is trashed - // at least that's what the MediaService implementation did - if (entity.Trashed) - db.Execute("DELETE FROM cmsContentXml WHERE nodeId=@id", new { id = entity.Id }); - - var xml = _entitySerializer.Serialize(entity).ToDataString(); - - var dto1 = new ContentXmlDto { NodeId = entity.Id, Xml = xml }; - OnRepositoryRefreshed(db, dto1); - } - - public void Handle(MemberRefreshNotification notification) - { - var db = Mock.Of(); // Notification no longer carries the scope, so we can't get the DB - var entity = notification.Entity; - - var xml = _entitySerializer.Serialize(entity).ToDataString(); - - var dto1 = new ContentXmlDto { NodeId = entity.Id, Xml = xml }; - OnRepositoryRefreshed(db, dto1); - } - - private static void OnRepositoryRefreshed(IUmbracoDatabase db, ContentXmlDto dto) - { - // use a custom SQL to update row version on each update - //db.InsertOrUpdate(dto); - - db.InsertOrUpdate(dto, - "SET xml=@xml, rv=rv+1 WHERE nodeId=@id", - new - { - xml = dto.Xml, - id = dto.NodeId - }); - } - - private static void OnRepositoryRefreshed(IUmbracoDatabase db, PreviewXmlDto dto) - { - // cannot simply update because of PetaPoco handling of the composite key ;-( - // read http://stackoverflow.com/questions/11169144/how-to-modify-petapoco-class-to-work-with-composite-key-comprising-of-non-numeri - // it works in https://github.com/schotime/PetaPoco and then https://github.com/schotime/NPoco but not here - // - // not important anymore as we don't manage version anymore, - // but: - // - // also - // use a custom SQL to update row version on each update - //db.InsertOrUpdate(dto); - - db.InsertOrUpdate(dto, - "SET xml=@xml, rv=rv+1 WHERE nodeId=@id", - new - { - xml = dto.Xml, - id = dto.NodeId, - }); - } - - public void Handle(ContentTypeRefreshedNotification notification) - { - const ContentTypeChangeTypes types // only for those that have been refreshed - = ContentTypeChangeTypes.RefreshMain | ContentTypeChangeTypes.RefreshOther | ContentTypeChangeTypes.Create; - var contentTypeIds = notification.Changes.Where(x => x.ChangeTypes.HasTypesAny(types)).Select(x => x.Item.Id).ToArray(); - if (contentTypeIds.Any()) - RebuildContentAndPreviewXml(contentTypeIds: contentTypeIds); - } - - public void Handle(MediaTypeRefreshedNotification notification) - { - const ContentTypeChangeTypes types // only for those that have been refreshed - = ContentTypeChangeTypes.RefreshMain | ContentTypeChangeTypes.RefreshOther | ContentTypeChangeTypes.Create; - var mediaTypeIds = notification.Changes.Where(x => x.ChangeTypes.HasTypesAny(types)).Select(x => x.Item.Id).ToArray(); - if (mediaTypeIds.Any()) - { - RebuildMediaXml(contentTypeIds: mediaTypeIds); - } - } - - public void Handle(MemberTypeRefreshedNotification notification) - { - const ContentTypeChangeTypes types // only for those that have been refreshed - = ContentTypeChangeTypes.RefreshMain | ContentTypeChangeTypes.RefreshOther | ContentTypeChangeTypes.Create; - var memberTypeIds = notification.Changes.Where(x => x.ChangeTypes.HasTypesAny(types)).Select(x => x.Item.Id).ToArray(); - if (memberTypeIds.Any()) - RebuildMemberXml(contentTypeIds: memberTypeIds); - } - - #endregion - - #region Rebuild Database Xml - - // RepositoryCacheMode.Scoped because we do NOT want to use the L2 cache that may be out-of-sync - // hopefully this does not cause issues and we're not nested in another scope w/different mode - // TODO: well, guess what? - // original code made sure the repository used no cache - // now we're using the Scoped scope cache mode - // and then? - - public void RebuildContentAndPreviewXml(int groupSize = 5000, IEnumerable contentTypeIds = null) - { - var contentTypeIdsA = contentTypeIds?.ToArray(); - - using (var scope = _scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.None)) - { - scope.WriteLock(Constants.Locks.ContentTree); - RebuildContentXmlLocked(scope, groupSize, contentTypeIdsA); - RebuildPreviewXmlLocked(scope, groupSize, contentTypeIdsA); - scope.Complete(); - } - } - - public void RebuildContentXml(int groupSize = 5000, IEnumerable contentTypeIds = null) - { - using (var scope = _scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.None)) - { - scope.WriteLock(Constants.Locks.ContentTree); - RebuildContentXmlLocked(scope, groupSize, contentTypeIds); - scope.Complete(); - } - } - - // assumes content tree lock - private void RebuildContentXmlLocked(IScope scope, int groupSize, IEnumerable contentTypeIds) - { - var contentTypeIdsA = contentTypeIds?.ToArray(); - var contentObjectType = Constants.ObjectTypes.Document; - var db = scope.Database; - - // remove all - if anything fails the transaction will rollback - if (contentTypeIds == null || contentTypeIdsA.Length == 0) - { - // must support SQL-CE - // db.Execute(@"DELETE cmsContentXml - //FROM cmsContentXml - //JOIN umbracoNode ON (cmsContentXml.nodeId=umbracoNode.Id) - //WHERE umbracoNode.nodeObjectType=@objType", - db.Execute(@"DELETE FROM cmsContentXml -WHERE cmsContentXml.nodeId IN ( - SELECT id FROM umbracoNode WHERE umbracoNode.nodeObjectType=@objType -)", - new { objType = contentObjectType }); - } - else - { - // assume number of ctypes won't blow IN(...) - // must support SQL-CE - // db.Execute(@"DELETE cmsContentXml - //FROM cmsContentXml - //JOIN umbracoNode ON (cmsContentXml.nodeId=umbracoNode.Id) - //JOIN {Constants.DatabaseSchema.Tables.Content} ON (cmsContentXml.nodeId={Constants.DatabaseSchema.Tables.Content}.nodeId) - //WHERE umbracoNode.nodeObjectType=@objType - //AND {Constants.DatabaseSchema.Tables.Content}.contentTypeId IN (@ctypes)", - db.Execute($@"DELETE FROM cmsContentXml -WHERE cmsContentXml.nodeId IN ( - SELECT id FROM umbracoNode - JOIN {Constants.DatabaseSchema.Tables.Content} ON {Constants.DatabaseSchema.Tables.Content}.nodeId=umbracoNode.id - WHERE umbracoNode.nodeObjectType=@objType - AND {Constants.DatabaseSchema.Tables.Content}.contentTypeId IN (@ctypes) -)", - new { objType = contentObjectType, ctypes = contentTypeIdsA }); - } - - // insert back - if anything fails the transaction will rollback - var query = scope.SqlContext.Query().Where(x => x.Published); - if (contentTypeIds != null && contentTypeIdsA.Length > 0) - query = query.WhereIn(x => x.ContentTypeId, contentTypeIdsA); // assume number of ctypes won't blow IN(...) - - long pageIndex = 0; - long processed = 0; - long total; - do - { - var descendants = _documentRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); - const bool published = true; // contentXml contains published content! - var items = descendants.Select(c => new ContentXmlDto { NodeId = c.Id, Xml = - _entitySerializer.Serialize(c, published).ToDataString() }).ToArray(); - db.BulkInsertRecords(items); - processed += items.Length; - } while (processed < total); - } - - public void RebuildPreviewXml(int groupSize = 5000, IEnumerable contentTypeIds = null) - { - using (var scope = _scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.None)) - { - scope.WriteLock(Constants.Locks.ContentTree); - RebuildPreviewXmlLocked(scope, groupSize, contentTypeIds); - scope.Complete(); - scope.Complete(); - } - } - - // assumes content tree lock - private void RebuildPreviewXmlLocked(IScope scope, int groupSize, IEnumerable contentTypeIds) - { - var contentTypeIdsA = contentTypeIds?.ToArray(); - var contentObjectType = Constants.ObjectTypes.Document; - var db = scope.Database; - - // remove all - if anything fails the transaction will rollback - if (contentTypeIds == null || contentTypeIdsA.Length == 0) - { - // must support SQL-CE - // db.Execute(@"DELETE cmsPreviewXml - //FROM cmsPreviewXml - //JOIN umbracoNode ON (cmsPreviewXml.nodeId=umbracoNode.Id) - //WHERE umbracoNode.nodeObjectType=@objType", - db.Execute(@"DELETE FROM cmsPreviewXml -WHERE cmsPreviewXml.nodeId IN ( - SELECT id FROM umbracoNode WHERE umbracoNode.nodeObjectType=@objType -)", - new { objType = contentObjectType }); - } - else - { - // assume number of ctypes won't blow IN(...) - // must support SQL-CE - // db.Execute(@"DELETE cmsPreviewXml - //FROM cmsPreviewXml - //JOIN umbracoNode ON (cmsPreviewXml.nodeId=umbracoNode.Id) - //JOIN {Constants.DatabaseSchema.Tables.Content} ON (cmsPreviewXml.nodeId={Constants.DatabaseSchema.Tables.Content}.nodeId) - //WHERE umbracoNode.nodeObjectType=@objType - //AND {Constants.DatabaseSchema.Tables.Content}.contentTypeId IN (@ctypes)", - db.Execute($@"DELETE FROM cmsPreviewXml -WHERE cmsPreviewXml.nodeId IN ( - SELECT id FROM umbracoNode - JOIN {Constants.DatabaseSchema.Tables.Content} ON {Constants.DatabaseSchema.Tables.Content}.nodeId=umbracoNode.id - WHERE umbracoNode.nodeObjectType=@objType - AND {Constants.DatabaseSchema.Tables.Content}.contentTypeId IN (@ctypes) -)", - new { objType = contentObjectType, ctypes = contentTypeIdsA }); - } - - // insert back - if anything fails the transaction will rollback - var query = scope.SqlContext.Query(); - if (contentTypeIds != null && contentTypeIdsA.Length > 0) - query = query.WhereIn(x => x.ContentTypeId, contentTypeIdsA); // assume number of ctypes won't blow IN(...) - - long pageIndex = 0; - long processed = 0; - long total; - do - { - // .GetPagedResultsByQuery implicitly adds ({Constants.DatabaseSchema.Tables.Document}.newest = 1) which - // is what we want for preview (ie latest version of a content, published or not) - var descendants = _documentRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); - const bool published = true; // previewXml contains edit content! - var items = descendants.Select(c => new PreviewXmlDto - { - NodeId = c.Id, - Xml = _entitySerializer.Serialize(c, published).ToDataString() - }).ToArray(); - db.BulkInsertRecords(items); - processed += items.Length; - } while (processed < total); - } - - public void RebuildMediaXml(int groupSize = 5000, IEnumerable contentTypeIds = null) - { - using (var scope = _scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.None)) - { - scope.WriteLock(Constants.Locks.MediaTree); - RebuildMediaXmlLocked(scope, groupSize, contentTypeIds); - scope.Complete(); - } - } - - // assumes media tree lock - public void RebuildMediaXmlLocked(IScope scope, int groupSize, IEnumerable contentTypeIds) - { - var contentTypeIdsA = contentTypeIds?.ToArray(); - var mediaObjectType = Constants.ObjectTypes.Media; - var db = scope.Database; - - // remove all - if anything fails the transaction will rollback - if (contentTypeIds == null || contentTypeIdsA.Length == 0) - { - // must support SQL-CE - // db.Execute(@"DELETE cmsContentXml - //FROM cmsContentXml - //JOIN umbracoNode ON (cmsContentXml.nodeId=umbracoNode.Id) - //WHERE umbracoNode.nodeObjectType=@objType", - db.Execute(@"DELETE FROM cmsContentXml -WHERE cmsContentXml.nodeId IN ( - SELECT id FROM umbracoNode WHERE umbracoNode.nodeObjectType=@objType -)", - new { objType = mediaObjectType }); - } - else - { - // assume number of ctypes won't blow IN(...) - // must support SQL-CE - // db.Execute(@"DELETE cmsContentXml - //FROM cmsContentXml - //JOIN umbracoNode ON (cmsContentXml.nodeId=umbracoNode.Id) - //JOIN {Constants.DatabaseSchema.Tables.Content} ON (cmsContentXml.nodeId={Constants.DatabaseSchema.Tables.Content}.nodeId) - //WHERE umbracoNode.nodeObjectType=@objType - //AND {Constants.DatabaseSchema.Tables.Content}.contentTypeId IN (@ctypes)", - db.Execute($@"DELETE FROM cmsContentXml -WHERE cmsContentXml.nodeId IN ( - SELECT id FROM umbracoNode - JOIN {Constants.DatabaseSchema.Tables.Content} ON {Constants.DatabaseSchema.Tables.Content}.nodeId=umbracoNode.id - WHERE umbracoNode.nodeObjectType=@objType - AND {Constants.DatabaseSchema.Tables.Content}.contentTypeId IN (@ctypes) -)", - new { objType = mediaObjectType, ctypes = contentTypeIdsA }); - } - - // insert back - if anything fails the transaction will rollback - var query = scope.SqlContext.Query(); - if (contentTypeIds != null && contentTypeIdsA.Length > 0) - query = query.WhereIn(x => x.ContentTypeId, contentTypeIdsA); // assume number of ctypes won't blow IN(...) - - long pageIndex = 0; - long processed = 0; - long total; - do - { - var descendants = _mediaRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); - var items = descendants.Select(m => new ContentXmlDto { NodeId = m.Id, Xml = - _entitySerializer.Serialize(m).ToDataString() }).ToArray(); - db.BulkInsertRecords(items); - processed += items.Length; - } while (processed < total); - } - - public void RebuildMemberXml(int groupSize = 5000, IEnumerable contentTypeIds = null) - { - using (var scope = _scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.None)) - { - scope.WriteLock(Constants.Locks.MemberTree); - RebuildMemberXmlLocked(scope, groupSize, contentTypeIds); - scope.Complete(); - } - } - - // assumes member tree lock - public void RebuildMemberXmlLocked(IScope scope, int groupSize, IEnumerable contentTypeIds) - { - var contentTypeIdsA = contentTypeIds?.ToArray(); - var memberObjectType = Constants.ObjectTypes.Member; - var db = scope.Database; - - // remove all - if anything fails the transaction will rollback - if (contentTypeIds == null || contentTypeIdsA.Length == 0) - { - // must support SQL-CE - // db.Execute(@"DELETE cmsContentXml - //FROM cmsContentXml - //JOIN umbracoNode ON (cmsContentXml.nodeId=umbracoNode.Id) - //WHERE umbracoNode.nodeObjectType=@objType", - db.Execute(@"DELETE FROM cmsContentXml -WHERE cmsContentXml.nodeId IN ( - SELECT id FROM umbracoNode WHERE umbracoNode.nodeObjectType=@objType -)", - new { objType = memberObjectType }); - } - else - { - // assume number of ctypes won't blow IN(...) - // must support SQL-CE - // db.Execute(@"DELETE cmsContentXml - //FROM cmsContentXml - //JOIN umbracoNode ON (cmsContentXml.nodeId=umbracoNode.Id) - //JOIN {Constants.DatabaseSchema.Tables.Content} ON (cmsContentXml.nodeId={Constants.DatabaseSchema.Tables.Content}.nodeId) - //WHERE umbracoNode.nodeObjectType=@objType - //AND {Constants.DatabaseSchema.Tables.Content}.contentTypeId IN (@ctypes)", - db.Execute($@"DELETE FROM cmsContentXml -WHERE cmsContentXml.nodeId IN ( - SELECT id FROM umbracoNode - JOIN {Constants.DatabaseSchema.Tables.Content} ON {Constants.DatabaseSchema.Tables.Content}.nodeId=umbracoNode.id - WHERE umbracoNode.nodeObjectType=@objType - AND {Constants.DatabaseSchema.Tables.Content}.contentTypeId IN (@ctypes) -)", - new { objType = memberObjectType, ctypes = contentTypeIdsA }); - } - - // insert back - if anything fails the transaction will rollback - var query = scope.SqlContext.Query(); - if (contentTypeIds != null && contentTypeIdsA.Length > 0) - query = query.WhereIn(x => x.ContentTypeId, contentTypeIdsA); // assume number of ctypes won't blow IN(...) - - long pageIndex = 0; - long processed = 0; - long total; - do - { - var descendants = _memberRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); - var items = descendants.Select(m => new ContentXmlDto { NodeId = m.Id, Xml = _entitySerializer.Serialize(m).ToDataString() }).ToArray(); - db.BulkInsertRecords(items); - processed += items.Length; - } while (processed < total); - } - - public bool VerifyContentAndPreviewXml() - { - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.ContentTree); - var ok = VerifyContentAndPreviewXmlLocked(scope); - scope.Complete(); - return ok; - } - } - - // assumes content tree lock - private static bool VerifyContentAndPreviewXmlLocked(IScope scope) - { - // every published content item should have a corresponding row in cmsContentXml - // every content item should have a corresponding row in cmsPreviewXml - // and that row should have the key="..." attribute - - var contentObjectType = Constants.ObjectTypes.Document; - var db = scope.Database; - - var count = db.ExecuteScalar($@"SELECT COUNT(*) -FROM umbracoNode -JOIN {Constants.DatabaseSchema.Tables.Document} ON (umbracoNode.id={Constants.DatabaseSchema.Tables.Document}.nodeId and {Constants.DatabaseSchema.Tables.Document}.published=1) -LEFT JOIN cmsContentXml ON (umbracoNode.id=cmsContentXml.nodeId) -WHERE umbracoNode.nodeObjectType=@objType -AND cmsContentXml.nodeId IS NULL OR cmsContentXml.xml NOT LIKE '% key=""' -", new { objType = contentObjectType }); - - if (count > 0) return false; - - count = db.ExecuteScalar(@"SELECT COUNT(*) -FROM umbracoNode -LEFT JOIN cmsPreviewXml ON (umbracoNode.id=cmsPreviewXml.nodeId) -WHERE umbracoNode.nodeObjectType=@objType -AND cmsPreviewXml.nodeId IS NULL OR cmsPreviewXml.xml NOT LIKE '% key=""' -", new { objType = contentObjectType }); - - return count == 0; - } - - public bool VerifyMediaXml() - { - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.MediaTree); - var ok = VerifyMediaXmlLocked(scope); - scope.Complete(); - return ok; - } - } - - // assumes media tree lock - public bool VerifyMediaXmlLocked(IScope scope) - { - // every non-trashed media item should have a corresponding row in cmsContentXml - // and that row should have the key="..." attribute - // TODO: where's the trashed test here? - - var mediaObjectType = Constants.ObjectTypes.Media; - var db = scope.Database; - - var count = db.ExecuteScalar($@"SELECT COUNT(*) -FROM umbracoNode -JOIN {Constants.DatabaseSchema.Tables.Document} ON (umbracoNode.id={Constants.DatabaseSchema.Tables.Document}.nodeId and {Constants.DatabaseSchema.Tables.Document}.published=1) -LEFT JOIN cmsContentXml ON (umbracoNode.id=cmsContentXml.nodeId) -WHERE umbracoNode.nodeObjectType=@objType -AND cmsContentXml.nodeId IS NULL OR cmsContentXml.xml NOT LIKE '% key=""' -", new { objType = mediaObjectType }); - - return count == 0; - } - - public bool VerifyMemberXml() - { - using (var scope = _scopeProvider.CreateScope()) - { - scope.ReadLock(Constants.Locks.MemberTree); - var ok = VerifyMemberXmlLocked(scope); - scope.Complete(); - return ok; - } - } - - // assumes member tree lock - public bool VerifyMemberXmlLocked(IScope scope) - { - // every member item should have a corresponding row in cmsContentXml - - var memberObjectType = Constants.ObjectTypes.Member; - var db = scope.Database; - - var count = db.ExecuteScalar(@"SELECT COUNT(*) -FROM umbracoNode -LEFT JOIN cmsContentXml ON (umbracoNode.id=cmsContentXml.nodeId) -WHERE umbracoNode.nodeObjectType=@objType -AND cmsContentXml.nodeId IS NULL -", new { objType = memberObjectType }); - - return count == 0; - } - - #endregion - } -} diff --git a/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs b/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs deleted file mode 100644 index 6029a069cb99..000000000000 --- a/tests/Umbraco.Tests/LegacyXmlPublishedCache/XmlStoreFilePersister.cs +++ /dev/null @@ -1,181 +0,0 @@ -using System; -using System.Threading; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core; -using Umbraco.Web.Scheduling; - -namespace Umbraco.Tests.LegacyXmlPublishedCache -{ - /// - /// This is the background task runner that persists the xml file to the file system - /// - /// - /// This is used so that all file saving is done on a web aware worker background thread and all logic is performed async so this - /// process will not interfere with any web requests threads. This is also done as to not require any global locks and to ensure that - /// if multiple threads are performing publishing tasks that the file will be persisted in accordance with the final resulting - /// xml structure since the file writes are queued. - /// - internal class XmlStoreFilePersister : LatchedBackgroundTaskBase - { - private readonly IBackgroundTaskRunner _runner; - private readonly ILogger _logger; - private readonly XmlStore _store; - private readonly object _locko = new object(); - private bool _released; - private Timer _timer; - private DateTime _initialTouch; - private readonly SystemLock _runLock = new SystemLock(); // ensure we run once at a time - - // note: - // as long as the runner controls the runs, we know that we run once at a time, but - // when the AppDomain goes down and the runner has completed and yet the persister is - // asked to save, then we need to run immediately - but the runner may be running, so - // we need to make sure there's no collision - hence _runLock - - private const int WaitMilliseconds = 4000; // save the cache 4s after the last change (ie every 4s min) - private const int MaxWaitMilliseconds = 30000; // save the cache after some time (ie no more than 30s of changes) - - // save the cache when the app goes down - public override bool RunsOnShutdown => _timer != null; - - // initialize the first instance, which is inactive (not touched yet) - public XmlStoreFilePersister(IBackgroundTaskRunner runner, XmlStore store, ILogger logger) - : this(runner, store, logger, false) - { } - - // initialize further instances, which are active (touched) - private XmlStoreFilePersister(IBackgroundTaskRunner runner, XmlStore store, ILogger logger, bool touched) - { - _runner = runner; - _store = store; - _logger = logger; - - if (_runner.TryAdd(this) == false) - { - _runner = null; // runner's down - _released = true; // don't mess with timer - return; - } - - // runner could decide to run it anytime now - - if (touched == false) return; - - _logger.LogDebug("Created, save in {WaitMilliseconds}ms.", WaitMilliseconds); - _initialTouch = DateTime.Now; - _timer = new Timer(_ => TimerRelease()); - _timer.Change(WaitMilliseconds, 0); - } - - public XmlStoreFilePersister Touch() - { - // if _released is false then we're going to setup a timer - // then the runner wants to shutdown & run immediately - // this sets _released to true & the timer will trigger eventually & who cares? - // if _released is true, either it's a normal release, or - // a runner shutdown, in which case we won't be able to - // add a new task, and so we'll run immediately - - var ret = this; - var runNow = false; - - lock (_locko) - { - if (_released) // our timer has triggered OR the runner is shutting down - { - _logger.LogDebug("Touched, was released..."); - - // release: has run or is running, too late, return a new task (adds itself to runner) - if (_runner == null) - { - _logger.LogDebug("Runner is down, run now."); - runNow = true; - } - else - { - _logger.LogDebug("Create new..."); - ret = new XmlStoreFilePersister(_runner, _store, _logger, true); - if (ret._runner == null) - { - // could not enlist with the runner, runner is completed, must run now - _logger.LogDebug("Runner is down, run now."); - runNow = true; - } - } - } - - else if (_timer == null) // we don't have a timer yet - { - _logger.LogDebug("Touched, was idle, start and save in {WaitMilliseconds}ms.", WaitMilliseconds); - _initialTouch = DateTime.Now; - _timer = new Timer(_ => TimerRelease()); - _timer.Change(WaitMilliseconds, 0); - } - - else // we have a timer - { - // change the timer to trigger in WaitMilliseconds unless we've been touched first more - // than MaxWaitMilliseconds ago and then leave the time unchanged - - if (DateTime.Now - _initialTouch < TimeSpan.FromMilliseconds(MaxWaitMilliseconds)) - { - _logger.LogDebug("Touched, was waiting, can delay, save in {WaitMilliseconds}ms.", WaitMilliseconds); - _timer.Change(WaitMilliseconds, 0); - } - else - { - _logger.LogDebug("Touched, was waiting, cannot delay."); - } - } - } - - // note: this comes from 7.x where it was not possible to lock the entire content service - // in our case, the XmlStore configures everything so that it is not possible to access content - // when going down, so this should never happen. - - if (runNow) - //Run(); - _logger.LogWarning("Cannot write now because we are going down, changes may be lost."); - - return ret; // this, by default, unless we created a new one - } - - private void TimerRelease() - { - lock (_locko) - { - _logger.LogDebug("Timer: release."); - _released = true; - - Release(); - } - } - - public override bool IsAsync => false; - - public override void Run() - { - lock (_locko) - { - _logger.LogDebug("Run now (sync)."); - // not really needed but safer (it's only us invoking Run, but the method is public...) - _released = true; - } - - using (_runLock.Lock()) - { - _store.SaveXmlToFile(); - } - } - - protected override void DisposeResources() - { - base.DisposeResources(); - - // stop the timer - if (_timer == null) return; - _timer.Change(Timeout.Infinite, Timeout.Infinite); - _timer.Dispose(); - } - } -} diff --git a/tests/Umbraco.Tests/Models/ContentXmlTest.cs b/tests/Umbraco.Tests/Models/ContentXmlTest.cs deleted file mode 100644 index 184bec85c235..000000000000 --- a/tests/Umbraco.Tests/Models/ContentXmlTest.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Linq; -using System.Xml.Linq; -using Microsoft.Extensions.DependencyInjection; -using NUnit.Framework; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; -using Umbraco.Tests.Testing; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.Models -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class ContentXmlTest : TestWithDatabaseBase - { - [Test] - public void Can_Generate_Xml_Representation_Of_Content() - { - // Arrange - var contentType = MockedContentTypes.CreateTextPageContentType(); - ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! - ServiceContext.ContentTypeService.Save(contentType); - - var content = MockedContent.CreateTextpageContent(contentType, "Root Home", -1); - ServiceContext.ContentService.Save(content, Constants.Security.SuperUserId); - - var nodeName = content.ContentType.Alias.ToSafeAlias(ShortStringHelper); - var urlName = content.GetUrlSegment(ShortStringHelper, new[]{new DefaultUrlSegmentProvider(ShortStringHelper) }); - - // Act - XElement element = content.ToXml(Factory.GetRequiredService()); - - // Assert - Assert.That(element, Is.Not.Null); - Assert.That(element.Name.LocalName, Is.EqualTo(nodeName)); - Assert.AreEqual(content.Id.ToString(), (string)element.Attribute("id")); - Assert.AreEqual(content.ParentId.ToString(), (string)element.Attribute("parentID")); - Assert.AreEqual(content.Level.ToString(), (string)element.Attribute("level")); - Assert.AreEqual(content.CreatorId.ToString(), (string)element.Attribute("creatorID")); - Assert.AreEqual(content.SortOrder.ToString(), (string)element.Attribute("sortOrder")); - Assert.AreEqual(content.CreateDate.ToString("s"), (string)element.Attribute("createDate")); - Assert.AreEqual(content.UpdateDate.ToString("s"), (string)element.Attribute("updateDate")); - Assert.AreEqual(content.Name, (string)element.Attribute("nodeName")); - Assert.AreEqual(urlName, (string)element.Attribute("urlName")); - Assert.AreEqual(content.Path, (string)element.Attribute("path")); - Assert.AreEqual("", (string)element.Attribute("isDoc")); - Assert.AreEqual(content.ContentType.Id.ToString(), (string)element.Attribute("nodeType")); - Assert.AreEqual(content.GetCreatorProfile(ServiceContext.UserService).Name, (string)element.Attribute("creatorName")); - Assert.AreEqual(content.GetWriterProfile(ServiceContext.UserService).Name, (string)element.Attribute("writerName")); - Assert.AreEqual(content.WriterId.ToString(), (string)element.Attribute("writerID")); - Assert.AreEqual(content.TemplateId.ToString(), (string)element.Attribute("template")); - - Assert.AreEqual(content.Properties["title"].GetValue().ToString(), element.Elements("title").Single().Value); - Assert.AreEqual(content.Properties["bodyText"].GetValue().ToString(), element.Elements("bodyText").Single().Value); - Assert.AreEqual(content.Properties["keywords"].GetValue().ToString(), element.Elements("keywords").Single().Value); - Assert.AreEqual(content.Properties["description"].GetValue().ToString(), element.Elements("description").Single().Value); - } - } -} diff --git a/tests/Umbraco.Tests/Models/MediaXmlTest.cs b/tests/Umbraco.Tests/Models/MediaXmlTest.cs deleted file mode 100644 index 6899ddca6519..000000000000 --- a/tests/Umbraco.Tests/Models/MediaXmlTest.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System.Linq; -using System.Xml.Linq; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.Models -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class MediaXmlTest : TestWithDatabaseBase - { - [Test] - public void Can_Generate_Xml_Representation_Of_Media() - { - // Arrange - var mediaType = MockedContentTypes.CreateImageMediaType("image2"); - ServiceContext.MediaTypeService.Save(mediaType); - - // reference, so static ctor runs, so event handlers register - // and then, this will reset the width, height... because the file does not exist, of course ;-( - var loggerFactory = NullLoggerFactory.Instance; - var scheme = Mock.Of(); - var contentSettings = new ContentSettings(); - - var mediaFileManager = new MediaFileManager(Mock.Of(), scheme, - loggerFactory.CreateLogger(), ShortStringHelper); - var ignored = new FileUploadPropertyEditor(DataValueEditorFactory, mediaFileManager, Microsoft.Extensions.Options.Options.Create(contentSettings), DataTypeService, LocalizationService, LocalizedTextService, UploadAutoFillProperties, ContentService); - - var media = MockedMedia.CreateMediaImage(mediaType, -1); - media.WriterId = -1; // else it's zero and that's not a user and it breaks the tests - ServiceContext.MediaService.Save(media, Constants.Security.SuperUserId); - - // so we have to force-reset these values because the property editor has cleared them - media.SetValue(Constants.Conventions.Media.Width, "200"); - media.SetValue(Constants.Conventions.Media.Height, "200"); - media.SetValue(Constants.Conventions.Media.Bytes, "100"); - media.SetValue(Constants.Conventions.Media.Extension, "png"); - - var nodeName = media.ContentType.Alias.ToSafeAlias(ShortStringHelper); - var urlName = media.GetUrlSegment(ShortStringHelper, new[] { new DefaultUrlSegmentProvider(ShortStringHelper) }); - - // Act - XElement element = media.ToXml(Factory.GetRequiredService()); - - // Assert - Assert.That(element, Is.Not.Null); - Assert.That(element.Name.LocalName, Is.EqualTo(nodeName)); - Assert.AreEqual(media.Id.ToString(), (string)element.Attribute("id")); - Assert.AreEqual(media.ParentId.ToString(), (string)element.Attribute("parentID")); - Assert.AreEqual(media.Level.ToString(), (string)element.Attribute("level")); - Assert.AreEqual(media.SortOrder.ToString(), (string)element.Attribute("sortOrder")); - Assert.AreEqual(media.CreateDate.ToString("s"), (string)element.Attribute("createDate")); - Assert.AreEqual(media.UpdateDate.ToString("s"), (string)element.Attribute("updateDate")); - Assert.AreEqual(media.Name, (string)element.Attribute("nodeName")); - Assert.AreEqual(urlName, (string)element.Attribute("urlName")); - Assert.AreEqual(media.Path, (string)element.Attribute("path")); - Assert.AreEqual("", (string)element.Attribute("isDoc")); - Assert.AreEqual(media.ContentType.Id.ToString(), (string)element.Attribute("nodeType")); - Assert.AreEqual(media.GetCreatorProfile(ServiceContext.UserService).Name, (string)element.Attribute("writerName")); - Assert.AreEqual(media.CreatorId.ToString(), (string)element.Attribute("writerID")); - Assert.IsNull(element.Attribute("template")); - - Assert.AreEqual(media.Properties[Constants.Conventions.Media.File].GetValue().ToString(), element.Elements(Constants.Conventions.Media.File).Single().Value); - Assert.AreEqual(media.Properties[Constants.Conventions.Media.Width].GetValue().ToString(), element.Elements(Constants.Conventions.Media.Width).Single().Value); - Assert.AreEqual(media.Properties[Constants.Conventions.Media.Height].GetValue().ToString(), element.Elements(Constants.Conventions.Media.Height).Single().Value); - Assert.AreEqual(media.Properties[Constants.Conventions.Media.Bytes].GetValue().ToString(), element.Elements(Constants.Conventions.Media.Bytes).Single().Value); - Assert.AreEqual(media.Properties[Constants.Conventions.Media.Extension].GetValue().ToString(), element.Elements(Constants.Conventions.Media.Extension).Single().Value); - } - } -} diff --git a/tests/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs b/tests/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs deleted file mode 100644 index 4da04e5e17f6..000000000000 --- a/tests/Umbraco.Tests/Persistence/FaultHandling/ConnectionRetryTest.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Data.SqlClient; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.Mappers; -using Umbraco.Tests.TestHelpers; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.Persistence.FaultHandling -{ - [TestFixture, Ignore("fixme - ignored test")] - public class ConnectionRetryTest - { - [Test] - public void Cant_Connect_To_SqlDatabase_With_Invalid_User() - { - const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=x;password=umbraco"; - const string providerName = Constants.DbProviderNames.SqlServer; - var factory = new UmbracoDatabaseFactory(Mock.Of>(), NullLoggerFactory.Instance, connectionString, providerName, new Lazy(() => Mock.Of()), TestHelper.DbProviderFactoryCreator, TestHelper.DatabaseSchemaCreatorFactory); - - using (var database = factory.CreateDatabase()) - { - Assert.Throws( - () => database.Fetch("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES")); - } - } - - [Test] - public void Cant_Connect_To_SqlDatabase_Because_Of_Network() - { - const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=umbraco;password=umbraco"; - const string providerName = Constants.DbProviderNames.SqlServer; - var factory = new UmbracoDatabaseFactory(Mock.Of>(), NullLoggerFactory.Instance, connectionString, providerName, new Lazy(() => Mock.Of()), TestHelper.DbProviderFactoryCreator, TestHelper.DatabaseSchemaCreatorFactory); - - using (var database = factory.CreateDatabase()) - { - Assert.Throws( - () => database.Fetch("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES")); - } - } - } -} diff --git a/tests/Umbraco.Tests/Persistence/Mappers/MapperTestBase.cs b/tests/Umbraco.Tests/Persistence/Mappers/MapperTestBase.cs deleted file mode 100644 index 446d407077dc..000000000000 --- a/tests/Umbraco.Tests/Persistence/Mappers/MapperTestBase.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Microsoft.Extensions.Options; -using Moq; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.Mappers; -using Umbraco.Cms.Persistence.SqlCe; - -namespace Umbraco.Tests.Persistence.Mappers -{ - public class MapperTestBase - { - protected Lazy MockSqlContext() - { - var sqlContext = Mock.Of(); - var syntax = new SqlCeSyntaxProvider(Options.Create(new GlobalSettings())); - Mock.Get(sqlContext).Setup(x => x.SqlSyntax).Returns(syntax); - return new Lazy(() => sqlContext); - } - - protected MapperConfigurationStore CreateMaps() - => new MapperConfigurationStore(); - } -} diff --git a/tests/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs b/tests/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs deleted file mode 100644 index c27218e1b3a4..000000000000 --- a/tests/Umbraco.Tests/Persistence/NPocoTests/PetaPocoCachesTest.cs +++ /dev/null @@ -1,195 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Infrastructure.Serialization; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.Services; -using Umbraco.Tests.TestHelpers.Entities; - -namespace Umbraco.Tests.Persistence.NPocoTests -{ - // FIXME: npoco - what shall we do with those tests? - // - [TestFixture, Ignore("fixme - ignored test")] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class PetaPocoCachesTest : TestWithSomeContentBase - { - -#if DEBUG - /// - /// This tests the peta poco caches - /// - /// - /// This test WILL fail. This is because we cannot stop PetaPoco from creating more cached items for queries such as - /// ContentTypeRepository.GetAll(1,2,3,4); - /// when combined with other GetAll queries that pass in an array of Ids, each query generated for different length - /// arrays will produce a unique query which then gets added to the cache. - /// - /// This test confirms this, if you analyze the DIFFERENCE output below you can see why the cached queries grow. - /// - //[Test] - //public void Check_Peta_Poco_Caches() - //{ - // var result = new List>>(); - - // Database.PocoData.UseLongKeys = true; - - // for (int i = 0; i < 2; i++) - // { - // int id1, id2, id3; - // string alias; - // CreateStuff(out id1, out id2, out id3, out alias); - // QueryStuff(id1, id2, id3, alias); - - // double totalBytes1; - // IEnumerable keys; - // Console.Write(PocoData.PrintDebugCacheReport(out totalBytes1, out keys)); - - // result.Add(new Tuple>(totalBytes1, keys.Count(), keys)); - // } - - // for (int index = 0; index < result.Count; index++) - // { - // var tuple = result[index]; - // Console.WriteLine("Bytes: {0}, Delegates: {1}", tuple.Item1, tuple.Item2); - // if (index != 0) - // { - // Console.WriteLine("----------------DIFFERENCE---------------------"); - // var diff = tuple.Item3.Except(result[index - 1].Item3); - // foreach (var d in diff) - // { - // Console.WriteLine(d); - // } - // } - - // } - - // var allByteResults = result.Select(x => x.Item1).Distinct(); - // var totalKeys = result.Select(x => x.Item2).Distinct(); - - // Assert.AreEqual(1, allByteResults.Count()); - // Assert.AreEqual(1, totalKeys.Count()); - //} - - //[Test] - //public void Verify_Memory_Expires() - //{ - // Database.PocoData.SlidingExpirationSeconds = 2; - - // var managedCache = new Database.ManagedCache(); - - // int id1, id2, id3; - // string alias; - // CreateStuff(out id1, out id2, out id3, out alias); - // QueryStuff(id1, id2, id3, alias); - - // var count1 = managedCache.GetCache().GetCount(); - // Console.WriteLine("Keys = " + count1); - // Assert.Greater(count1, 0); - - // Thread.Sleep(10000); - - // var count2 = managedCache.GetCache().GetCount(); - // Console.WriteLine("Keys = " + count2); - // Assert.Less(count2, count1); - //} - - private void QueryStuff(int id1, int id2, int id3, string alias1) - { - var contentService = ServiceContext.ContentService; - - ServiceContext.TagService.GetTagsForEntity(id1); - - ServiceContext.TagService.GetAllContentTags(); - - ServiceContext.TagService.GetTagsForEntity(id2); - - ServiceContext.TagService.GetTagsForEntity(id3); - - contentService.CountDescendants(id3); - - contentService.CountChildren(id3); - - contentService.Count(documentTypeAlias: alias1); - - contentService.Count(); - - contentService.GetById(Guid.NewGuid()); - - contentService.GetByLevel(2); - - contentService.GetVersions(id3); - - contentService.GetRootContent(); - - contentService.GetContentForExpiration(DateTime.Now); - - contentService.GetContentForRelease(DateTime.Now); - - ((ContentService)contentService).GetPublishedDescendants(new Content("Test", -1, new ContentType(ShortStringHelper, -1)) - { - Id = id1, - Path = "-1," + id1 - }); - - contentService.GetVersion(1234); - } - - private void CreateStuff(out int id1, out int id2, out int id3, out string alias) - { - var contentService = ServiceContext.ContentService; - var serializer = new JsonNetSerializer(); - - var ctAlias = "umbTextpage" + Guid.NewGuid().ToString("N"); - alias = ctAlias; - - for (int i = 0; i < 20; i++) - { - contentService.CreateAndSave("Test", -1, "umbTextpage", 0); - } - var contentTypeService = ServiceContext.ContentTypeService; - var contentType = MockedContentTypes.CreateSimpleContentType(ctAlias, "test Doc Type"); - contentTypeService.Save(contentType); - for (int i = 0; i < 20; i++) - { - contentService.CreateAndSave("Test", -1, ctAlias, 0); - } - var parent = contentService.CreateAndSave("Test", -1, ctAlias, 0); - id1 = parent.Id; - - for (int i = 0; i < 20; i++) - { - contentService.CreateAndSave("Test", parent, ctAlias); - } - IContent current = parent; - for (int i = 0; i < 20; i++) - { - current = contentService.CreateAndSave("Test", current, ctAlias); - } - contentType = MockedContentTypes.CreateSimpleContentType("umbMandatory" + Guid.NewGuid().ToString("N"), "Mandatory Doc Type", true); - contentType.PropertyGroups.First().PropertyTypes.Add( - new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext, "tags") - { - DataTypeId = 1041 - }); - contentTypeService.Save(contentType); - var content1 = MockedContent.CreateSimpleContent(contentType, "Tagged content 1", -1); - content1.AssignTags(PropertyEditorCollection.Value, DataTypeService, serializer, "tags", new[] { "hello", "world", "some", "tags" }); - content1.PublishCulture(CultureImpact.Invariant); - contentService.SaveAndPublish(content1); - id2 = content1.Id; - - var content2 = MockedContent.CreateSimpleContent(contentType, "Tagged content 2", -1); - content2.AssignTags(PropertyEditorCollection.Value, DataTypeService, serializer, "tags", new[] { "hello", "world", "some", "tags" }); - content2.PublishCulture(CultureImpact.Invariant); - contentService.SaveAndPublish(content2); - id3 = content2.Id; - - contentService.MoveToRecycleBin(content1); - } -#endif - } -} diff --git a/tests/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs b/tests/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs deleted file mode 100644 index fcf64641c8da..000000000000 --- a/tests/Umbraco.Tests/Persistence/Querying/ContentTypeSqlMappingTests.cs +++ /dev/null @@ -1,199 +0,0 @@ -// fixme - does it make any sense to keep these tests here? -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using NPoco; -//using NUnit.Framework; -//using Umbraco.Core; -//using Umbraco.Core.Models; -//using Umbraco.Core.Persistence.Dtos; -//using Umbraco.Core.Persistence.Repositories; -//using Umbraco.Core.Persistence.Repositories.Implement; -//using Umbraco.Tests.TestHelpers; -//using Umbraco.Tests.Testing; - -//namespace Umbraco.Tests.Persistence.Querying -//{ -// [TestFixture] -// [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] -// public class ContentTypeSqlMappingTests : TestWithDatabaseBase -// { -// [Test] -// public void Can_Map_Content_Type_Templates_And_Allowed_Types() -// { -// IDictionary.AssociatedTemplate>> allAssociatedTemplates; -// IDictionary> allParentContentTypeIds; -// IContentType[] contentTypes; - -// using (var scope = ScopeProvider.CreateScope()) -// { -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntax.GetQuotedTableName("umbracoNode")))); -// scope.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 55554, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,55554", SortOrder = 1, UniqueId = new Guid("87D1EAB6-AB27-4852-B3DF-DE8DBA4A1AA0"), Text = "Template 1", NodeObjectType = Constants.ObjectTypes.Template, CreateDate = DateTime.Now }); -// scope.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 55555, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,55555", SortOrder = 1, UniqueId = new Guid("3390BDF4-C974-4211-AA95-3812A8CE7C46"), Text = "Template 2", NodeObjectType = Constants.ObjectTypes.Template, CreateDate = DateTime.Now }); -// scope.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99997, Trashed = false, ParentId = -1, UserId = -1, Level = 0, Path = "-1,99997", SortOrder = 0, UniqueId = new Guid("BB3241D5-6842-4EFA-A82A-5F56885CF528"), Text = "Test Content Type 1", NodeObjectType = Constants.ObjectTypes.DocumentType, CreateDate = DateTime.Now }); -// scope.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99998, Trashed = false, ParentId = -1, UserId = -1, Level = 0, Path = "-1,99998", SortOrder = 0, UniqueId = new Guid("EEA66B06-302E-49BA-A8B2-EDF07248BC59"), Text = "Test Content Type 2", NodeObjectType = Constants.ObjectTypes.DocumentType, CreateDate = DateTime.Now }); -// scope.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99999, Trashed = false, ParentId = -1, UserId = -1, Level = 0, Path = "-1,99999", SortOrder = 0, UniqueId = new Guid("C45CC083-BB27-4C1C-B448-6F703CC9B799"), Text = "Test Content Type 2", NodeObjectType = Constants.ObjectTypes.DocumentType, CreateDate = DateTime.Now }); -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntax.GetQuotedTableName("umbracoNode")))); - -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntax.GetQuotedTableName("cmsTemplate")))); -// scope.Database.Insert("cmsTemplate", "pk", false, new TemplateDto { NodeId = 55554, Alias = "testTemplate1", PrimaryKey = 22221}); -// scope.Database.Insert("cmsTemplate", "pk", false, new TemplateDto { NodeId = 55555, Alias = "testTemplate2", PrimaryKey = 22222 }); -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntax.GetQuotedTableName("cmsTemplate")))); - -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntax.GetQuotedTableName("cmsContentType")))); -// scope.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88887, NodeId = 99997, Alias = "TestContentType1", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true }); -// scope.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88888, NodeId = 99998, Alias = "TestContentType2", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true }); -// scope.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88889, NodeId = 99999, Alias = "TestContentType3", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true }); -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntax.GetQuotedTableName("cmsContentType")))); - -// scope.Database.Insert(new ContentTypeTemplateDto { ContentTypeNodeId = 99997, IsDefault = true, TemplateNodeId = 55555 }); -// scope.Database.Insert(new ContentTypeTemplateDto { ContentTypeNodeId = 99997, IsDefault = false, TemplateNodeId = 55554 }); - -// scope.Database.Insert(new ContentTypeAllowedContentTypeDto { AllowedId = 99998, Id = 99997, SortOrder = 1 }); -// scope.Database.Insert(new ContentTypeAllowedContentTypeDto { AllowedId = 99999, Id = 99997, SortOrder = 2}); - -// scope.Database.Insert(new ContentType2ContentTypeDto { ChildId = 99999, ParentId = 99997}); -// scope.Database.Insert(new ContentType2ContentTypeDto { ChildId = 99998, ParentId = 99997 }); - -// contentTypes = ContentTypeQueryMapper.GetContentTypes( -// scope.Database, out allAssociatedTemplates, out allParentContentTypeIds) -// .Where(x => new[] { 99997, 99998 }.Contains(x.Id)) -// .ToArray(); - -// scope.Complete(); -// } - -// var contentType1 = contentTypes.SingleOrDefault(x => x.Id == 99997); -// Assert.IsNotNull(contentType1); - -// var associatedTemplates1 = allAssociatedTemplates[contentType1.Id]; -// var parentContentTypes1 = allParentContentTypeIds[contentType1.Id]; - -// Assert.AreEqual(2, contentType1.AllowedContentTypes.Count()); -// Assert.AreEqual(2, associatedTemplates1.Count()); -// Assert.AreEqual(0, parentContentTypes1.Count()); - -// var contentType2 = contentTypes.SingleOrDefault(x => x.Id == 99998); -// Assert.IsNotNull(contentType2); - -// var associatedTemplates2 = allAssociatedTemplates[contentType2.Id]; -// var parentContentTypes2 = allParentContentTypeIds[contentType2.Id]; - -// Assert.AreEqual(0, contentType2.AllowedContentTypes.Count()); -// Assert.AreEqual(0, associatedTemplates2.Count()); -// Assert.AreEqual(1, parentContentTypes2.Count()); -// } - -// [Test] -// public void Can_Map_Media_Type_And_Allowed_Types() -// { -// IDictionary> allParentContentTypeIds; -// IMediaType[] contentTypes; - -// using (var scope = ScopeProvider.CreateScope()) -// { -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntax.GetQuotedTableName("umbracoNode")))); -// scope.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99997, Trashed = false, ParentId = -1, UserId = -1, Level = 0, Path = "-1,99997", SortOrder = 0, UniqueId = new Guid("BB3241D5-6842-4EFA-A82A-5F56885CF528"), Text = "Test Media Type 1", NodeObjectType = Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); -// scope.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99998, Trashed = false, ParentId = -1, UserId = -1, Level = 0, Path = "-1,99998", SortOrder = 0, UniqueId = new Guid("EEA66B06-302E-49BA-A8B2-EDF07248BC59"), Text = "Test Media Type 2", NodeObjectType = Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); -// scope.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99999, Trashed = false, ParentId = -1, UserId = -1, Level = 0, Path = "-1,99999", SortOrder = 0, UniqueId = new Guid("C45CC083-BB27-4C1C-B448-6F703CC9B799"), Text = "Test Media Type 2", NodeObjectType = Constants.ObjectTypes.MediaType, CreateDate = DateTime.Now }); -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntax.GetQuotedTableName("umbracoNode")))); - -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntax.GetQuotedTableName("cmsContentType")))); -// scope.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88887, NodeId = 99997, Alias = "TestContentType1", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true }); -// scope.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88888, NodeId = 99998, Alias = "TestContentType2", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true }); -// scope.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88889, NodeId = 99999, Alias = "TestContentType3", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true }); -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntax.GetQuotedTableName("cmsContentType")))); - -// scope.Database.Insert(new ContentTypeAllowedContentTypeDto { AllowedId = 99998, Id = 99997, SortOrder = 1 }); -// scope.Database.Insert(new ContentTypeAllowedContentTypeDto { AllowedId = 99999, Id = 99997, SortOrder = 2 }); - -// scope.Database.Insert(new ContentType2ContentTypeDto { ChildId = 99999, ParentId = 99997 }); -// scope.Database.Insert(new ContentType2ContentTypeDto { ChildId = 99998, ParentId = 99997 }); - -// contentTypes = ContentTypeQueryMapper.MapMediaTypes( -// scope.Database, SqlSyntax, out allParentContentTypeIds) -// .Where(x => (new[] { 99997, 99998 }).Contains(x.Id)) -// .ToArray(); - -// scope.Complete(); -// } - -// var contentType1 = contentTypes.SingleOrDefault(x => x.Id == 99997); -// Assert.IsNotNull(contentType1); - -// var parentContentTypes1 = allParentContentTypeIds[contentType1.Id]; - -// Assert.AreEqual(2, contentType1.AllowedContentTypes.Count()); -// Assert.AreEqual(0, parentContentTypes1.Count()); - -// var contentType2 = contentTypes.SingleOrDefault(x => x.Id == 99998); -// Assert.IsNotNull(contentType2); - -// var parentContentTypes2 = allParentContentTypeIds[contentType2.Id]; - -// Assert.AreEqual(0, contentType2.AllowedContentTypes.Count()); -// Assert.AreEqual(1, parentContentTypes2.Count()); - -// } - -// [Test] -// public void Can_Map_All_Property_Groups_And_Types() -// { -// IDictionary allPropTypeCollection; -// IDictionary allPropGroupCollection; - -// using (var scope = ScopeProvider.CreateScope()) -// { -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntax.GetQuotedTableName("umbracoNode")))); -// scope.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 55555, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,55555", SortOrder = 1, UniqueId = new Guid("3390BDF4-C974-4211-AA95-3812A8CE7C46"), Text = "Test Data Type", NodeObjectType = Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); -// scope.Database.Insert("umbracoNode", "id", false, new NodeDto { NodeId = 99999, Trashed = false, ParentId = -1, UserId = -1, Level = 0, Path = "-1,99999", SortOrder = 0, UniqueId = new Guid("129241F0-D24E-4FC3-92D1-BC2D48B7C431"), Text = "Test Content Type", NodeObjectType = Constants.ObjectTypes.DocumentType, CreateDate = DateTime.Now }); -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntax.GetQuotedTableName("umbracoNode")))); - -// scope.Database.Insert(Constants.DatabaseSchema.Tables.DataType, "pk", false, new DataTypeDto { NodeId = 55555, EditorAlias = Constants.PropertyEditors.Aliases.TextBox, DbType = "Nvarchar" }); - -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntax.GetQuotedTableName("cmsContentType")))); -// scope.Database.Insert("cmsContentType", "pk", false, new ContentTypeDto { PrimaryKey = 88888, NodeId = 99999, Alias = "TestContentType", Icon = "icon-folder", Thumbnail = "folder.png", IsContainer = false, AllowAtRoot = true }); -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntax.GetQuotedTableName("cmsContentType")))); - -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntax.GetQuotedTableName("cmsPropertyTypeGroup")))); -// scope.Database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 77776, UniqueId = 77776.ToGuid(), ContentTypeNodeId = 99999, Text = "Group1", SortOrder = 1 }); -// scope.Database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 77777, UniqueId = 77777.ToGuid(), ContentTypeNodeId = 99999, Text = "Group2", SortOrder = 2 }); -// scope.Database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 77778, UniqueId = 77778.ToGuid(), ContentTypeNodeId = 99999, Text = "Group3", SortOrder = 3 }); -// scope.Database.Insert("cmsPropertyTypeGroup", "id", false, new PropertyTypeGroupDto { Id = 77779, UniqueId = 77779.ToGuid(), ContentTypeNodeId = 99999, Text = "Group4", SortOrder = 4 }); -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntax.GetQuotedTableName("cmsPropertyTypeGroup")))); - -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntax.GetQuotedTableName("cmsPropertyType")))); -// scope.Database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 66662, UniqueId = 66662.ToGuid(), DataTypeId = 55555, ContentTypeId = 99999, PropertyTypeGroupId = 77776, Alias = "property1", Name = "Property 1", SortOrder = 0, Mandatory = false, ValidationRegExp = null, Description = null }); -// scope.Database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 66663, UniqueId = 66663.ToGuid(), DataTypeId = 55555, ContentTypeId = 99999, PropertyTypeGroupId = 77776, Alias = "property2", Name = "Property 2", SortOrder = 1, Mandatory = false, ValidationRegExp = null, Description = null }); -// scope.Database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 66664, UniqueId = 66664.ToGuid(), DataTypeId = 55555, ContentTypeId = 99999, PropertyTypeGroupId = 77777, Alias = "property3", Name = "Property 3", SortOrder = 2, Mandatory = false, ValidationRegExp = null, Description = null }); -// scope.Database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 66665, UniqueId = 66665.ToGuid(), DataTypeId = 55555, ContentTypeId = 99999, PropertyTypeGroupId = 77777, Alias = "property4", Name = "Property 4", SortOrder = 3, Mandatory = false, ValidationRegExp = null, Description = null }); -// scope.Database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 66666, UniqueId = 66666.ToGuid(), DataTypeId = 55555, ContentTypeId = 99999, PropertyTypeGroupId = null, Alias = "property5", Name = "Property 5", SortOrder = 4, Mandatory = false, ValidationRegExp = null, Description = null }); -// scope.Database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 66667, UniqueId = 66667.ToGuid(), DataTypeId = 55555, ContentTypeId = 99999, PropertyTypeGroupId = 77778, Alias = "property6", Name = "Property 6", SortOrder = 5, Mandatory = false, ValidationRegExp = null, Description = null }); -// scope.Database.Insert("cmsPropertyType", "id", false, new PropertyTypeDto { Id = 66668, UniqueId = 66668.ToGuid(), DataTypeId = 55555, ContentTypeId = 99999, PropertyTypeGroupId = 77778, Alias = "property7", Name = "Property 7", SortOrder = 6, Mandatory = false, ValidationRegExp = null, Description = null }); -// scope.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntax.GetQuotedTableName("cmsPropertyType")))); - -// ContentTypeQueryMapper.MapGroupsAndProperties_(new[] { 99999 }, scope.Database, SqlSyntax, true, out allPropTypeCollection, out allPropGroupCollection); - -// scope.Complete(); -// } - -// var propGroupCollection = allPropGroupCollection[99999]; -// var propTypeCollection = allPropTypeCollection[99999]; - -// Assert.AreEqual(4, propGroupCollection.Count); -// Assert.AreEqual(2, propGroupCollection["Group1"].PropertyTypes.Count); -// Assert.IsTrue(propGroupCollection["Group1"].PropertyTypes.Contains("property1")); -// Assert.IsTrue(propGroupCollection["Group1"].PropertyTypes.Contains("property2")); -// Assert.AreEqual(2, propGroupCollection["Group2"].PropertyTypes.Count); -// Assert.IsTrue(propGroupCollection["Group2"].PropertyTypes.Contains("property3")); -// Assert.IsTrue(propGroupCollection["Group2"].PropertyTypes.Contains("property4")); -// Assert.AreEqual(2, propGroupCollection["Group3"].PropertyTypes.Count); -// Assert.IsTrue(propGroupCollection["Group3"].PropertyTypes.Contains("property6")); -// Assert.IsTrue(propGroupCollection["Group3"].PropertyTypes.Contains("property7")); -// Assert.AreEqual(0, propGroupCollection["Group4"].PropertyTypes.Count); - -// Assert.AreEqual(1, propTypeCollection.Count); -// Assert.IsTrue(propTypeCollection.Contains("property5")); -// } -// } -//} diff --git a/tests/Umbraco.Tests/Plugins/PluginManagerTests.cs b/tests/Umbraco.Tests/Plugins/PluginManagerTests.cs deleted file mode 100644 index 44879eae2fa6..000000000000 --- a/tests/Umbraco.Tests/Plugins/PluginManagerTests.cs +++ /dev/null @@ -1,392 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using Moq; -using NUnit.Framework; -using SqlCE4Umbraco; -using umbraco; -using umbraco.businesslogic; -using umbraco.cms.businesslogic; -using Umbraco.Core; -using Umbraco.Core.Cache; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Profiling; -using Umbraco.Core.PropertyEditors; -using umbraco.DataLayer; -using umbraco.editorControls; -using umbraco.interfaces; -using umbraco.MacroEngines; -using umbraco.uicontrols; -using Umbraco.Web; -using Umbraco.Web.PropertyEditors; - -namespace Umbraco.Tests.Plugins -{ - - [TestFixture] - public class PluginManagerTests - { - private PluginManager _manager; - [SetUp] - public void Initialize() - { - //this ensures its reset - _manager = new PluginManager(new ActivatorServiceProvider(), new NullCacheProvider(), - new ProfilingLogger(Mock.Of(), Mock.Of())); - - //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver - //TODO: Should probably update this so it only searches this assembly and add custom types to be found - _manager.AssembliesToScan = new[] - { - this.GetType().Assembly, - typeof(ApplicationStartupHandler).Assembly, - typeof(SqlCEHelper).Assembly, - typeof(CMSNode).Assembly, - typeof(System.Guid).Assembly, - typeof(NUnit.Framework.Assert).Assembly, - typeof(Microsoft.CSharp.CSharpCodeProvider).Assembly, - typeof(System.Xml.NameTable).Assembly, - typeof(System.Configuration.GenericEnumConverter).Assembly, - typeof(System.Web.SiteMap).Assembly, - typeof(TabPage).Assembly, - typeof(System.Web.Mvc.ActionResult).Assembly, - typeof(TypeFinder).Assembly, - typeof(ISqlHelper).Assembly, - typeof(ICultureDictionary).Assembly, - typeof(UmbracoContext).Assembly, - typeof(BaseDataType).Assembly - }; - } - - [TearDown] - public void TearDown() - { - _manager = null; - } - - private DirectoryInfo PrepareFolder() - { - var assDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory; - var dir = Directory.CreateDirectory(Path.Combine(assDir.FullName, "PluginManager", Guid.NewGuid().ToString("N"))); - foreach (var f in dir.GetFiles()) - { - f.Delete(); - } - return dir; - } - - //[Test] - //public void Scan_Vs_Load_Benchmark() - //{ - // var pluginManager = new PluginManager(false); - // var watch = new Stopwatch(); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var type2 = Type.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type3 = Type.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type4 = Type.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type5 = Type.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var type2 = BuildManager.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type3 = BuildManager.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type4 = BuildManager.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type5 = BuildManager.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); - // watch.Reset(); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var refreshers = pluginManager.ResolveTypes(false); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (2nd round): " + watch.ElapsedMilliseconds); - //} - - ////NOTE: This test shows that Type.GetType is 100% faster than Assembly.Load(..).GetType(...) so we'll use that :) - //[Test] - //public void Load_Type_Benchmark() - //{ - // var watch = new Stopwatch(); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var type2 = Type.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type3 = Type.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type4 = Type.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type5 = Type.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); - // watch.Reset(); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var type2 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") - // .GetType("umbraco.macroCacheRefresh"); - // var type3 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") - // .GetType("umbraco.templateCacheRefresh"); - // var type4 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") - // .GetType("umbraco.presentation.cache.MediaLibraryRefreshers"); - // var type5 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") - // .GetType("umbraco.presentation.cache.pageRefresher"); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (2nd round): " + watch.ElapsedMilliseconds); - // watch.Reset(); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var type2 = BuildManager.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type3 = BuildManager.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type4 = BuildManager.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type5 = BuildManager.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); - //} - - [Test] - public void Detect_Legacy_Plugin_File_List() - { - var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); - - var filePath= Path.Combine(tempFolder, string.Format("umbraco-plugins.{0}.list", NetworkHelper.FileSafeMachineName)); - - File.WriteAllText(filePath, @" - - - - -"); - - Assert.IsEmpty(_manager.ReadCache()); // uber-legacy cannot be read - - File.Delete(filePath); - - File.WriteAllText(filePath, @" - - - - -"); - - Assert.IsEmpty(_manager.ReadCache()); // legacy cannot be read - - File.Delete(filePath); - - File.WriteAllText(filePath, @"IContentFinder - -MyContentFinder -AnotherContentFinder - -"); - - Assert.IsNotNull(_manager.ReadCache()); // works - } - - [Test] - public void Create_Cached_Plugin_File() - { - var types = new[] { typeof (PluginManager), typeof (PluginManagerTests), typeof (UmbracoContext) }; - - var typeList1 = new PluginManager.TypeList(typeof (object), null); - foreach (var type in types) typeList1.Add(type); - _manager.AddTypeList(typeList1); - _manager.WriteCache(); - - var plugins = _manager.TryGetCached(typeof (object), null); - var diffType = _manager.TryGetCached(typeof (object), typeof (ObsoleteAttribute)); - - Assert.IsTrue(plugins.Success); - //this will be false since there is no cache of that type resolution kind - Assert.IsFalse(diffType.Success); - - Assert.AreEqual(3, plugins.Result.Count()); - var shouldContain = types.Select(x => x.AssemblyQualifiedName); - //ensure they are all found - Assert.IsTrue(plugins.Result.ContainsAll(shouldContain)); - } - - [Test] - public void Get_Plugins_Hash() - { - //Arrange - var dir = PrepareFolder(); - var d1 = dir.CreateSubdirectory("1"); - var d2 = dir.CreateSubdirectory("2"); - var d3 = dir.CreateSubdirectory("3"); - var d4 = dir.CreateSubdirectory("4"); - var f1 = new FileInfo(Path.Combine(d1.FullName, "test1.dll")); - var f2 = new FileInfo(Path.Combine(d1.FullName, "test2.dll")); - var f3 = new FileInfo(Path.Combine(d2.FullName, "test1.dll")); - var f4 = new FileInfo(Path.Combine(d2.FullName, "test2.dll")); - var f5 = new FileInfo(Path.Combine(d3.FullName, "test1.dll")); - var f6 = new FileInfo(Path.Combine(d3.FullName, "test2.dll")); - var f7 = new FileInfo(Path.Combine(d4.FullName, "test1.dll")); - f1.CreateText().Close(); - f2.CreateText().Close(); - f3.CreateText().Close(); - f4.CreateText().Close(); - f5.CreateText().Close(); - f6.CreateText().Close(); - f7.CreateText().Close(); - var list1 = new[] { f1, f2, f3, f4, f5, f6 }; - var list2 = new[] { f1, f3, f5 }; - var list3 = new[] { f1, f3, f5, f7 }; - - //Act - var hash1 = PluginManager.GetFileHash(list1, new ProfilingLogger(Mock.Of(), Mock.Of())); - var hash2 = PluginManager.GetFileHash(list2, new ProfilingLogger(Mock.Of(), Mock.Of())); - var hash3 = PluginManager.GetFileHash(list3, new ProfilingLogger(Mock.Of(), Mock.Of())); - - //Assert - Assert.AreNotEqual(hash1, hash2); - Assert.AreNotEqual(hash1, hash3); - Assert.AreNotEqual(hash2, hash3); - - Assert.AreEqual(hash1, PluginManager.GetFileHash(list1, new ProfilingLogger(Mock.Of(), Mock.Of()))); - } - - [Test] - public void Ensure_Only_One_Type_List_Created() - { - var foundTypes1 = _manager.ResolveFindMeTypes(); - var foundTypes2 = _manager.ResolveFindMeTypes(); - Assert.AreEqual(1, _manager.TypeLists.Count(x => x.BaseType == typeof(IFindMe) && x.AttributeType == null)); - } - - [Test] - public void Resolves_Assigned_Mappers() - { - var foundTypes1 = _manager.ResolveAssignedMapperTypes(); - Assert.AreEqual(31, foundTypes1.Count()); - } - - [Test] - public void Resolves_Types() - { - var foundTypes1 = _manager.ResolveFindMeTypes(); - Assert.AreEqual(2, foundTypes1.Count()); - } - - [Test] - public void Resolves_Attributed_Trees() - { - var trees = _manager.ResolveAttributedTrees(); - Assert.AreEqual(5, trees.Count()); - } - - [Test] - public void Resolves_Actions() - { - var actions = _manager.ResolveActions(); - Assert.AreEqual(38, actions.Count()); - } - - [Test] - public void Resolves_Trees() - { - var trees = _manager.ResolveTrees(); - Assert.AreEqual(33, trees.Count()); - } - - [Test] - public void Resolves_Applications() - { - var apps = _manager.ResolveApplications(); - Assert.AreEqual(7, apps.Count()); - } - - [Test] - public void Resolves_DataTypes() - { - var types = _manager.ResolveDataTypes(); - Assert.AreEqual(35, types.Count()); - } - - [Test] - public void Resolves_RazorDataTypeModels() - { - var types = _manager.ResolveRazorDataTypeModels(); - Assert.AreEqual(2, types.Count()); - } - - [Test] - public void Resolves_RestExtensions() - { - var types = _manager.ResolveRestExtensions(); - Assert.AreEqual(2, types.Count()); - } - - [Test] - public void Resolves_XsltExtensions() - { - var types = _manager.ResolveXsltExtensions(); - Assert.AreEqual(3, types.Count()); - } - - /// - /// This demonstrates this issue: http://issues.umbraco.org/issue/U4-3505 - the TypeList was returning a list of assignable types - /// not explicit types which is sort of ideal but is confusing so we'll do it the less confusing way. - /// - [Test] - public void TypeList_Resolves_Explicit_Types() - { - var types = new HashSet(); - - var propEditors = new PluginManager.TypeList(typeof (PropertyEditor), null); - propEditors.Add(typeof(LabelPropertyEditor)); - types.Add(propEditors); - - var found = types.SingleOrDefault(x => x.BaseType == typeof (PropertyEditor) && x.AttributeType == null); - - Assert.IsNotNull(found); - - //This should not find a type list of this type - var shouldNotFind = types.SingleOrDefault(x => x.BaseType == typeof (IParameterEditor) && x.AttributeType == null); - - Assert.IsNull(shouldNotFind); - } - - [XsltExtension("Blah.Blah")] - public class MyXsltExtension - { - - } - - - [Umbraco.Web.BaseRest.RestExtension("Blah")] - public class MyRestExtesion - { - - } - - public interface IFindMe : IDiscoverable - { - - } - - public class FindMe1 : IFindMe - { - - } - - public class FindMe2 : IFindMe - { - - } - - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/tests/Umbraco.Tests/PublishedContent/NuCacheTests.cs deleted file mode 100644 index 3f1bd0963975..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ /dev/null @@ -1,316 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Linq; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Changes; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Infrastructure.PublishedCache; -using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; -using Umbraco.Cms.Infrastructure.Serialization; -using Umbraco.Cms.Tests.Common; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing.Objects; -using Umbraco.Web.Composing; - -namespace Umbraco.Tests.PublishedContent -{ - [TestFixture] - public class NuCacheTests - { - private IPublishedSnapshotService _snapshotService; - private IVariationContextAccessor _variationAccesor; - private IContentCacheDataSerializerFactory _contentNestedDataSerializerFactory; - private ContentType _contentType; - private PropertyType _propertyType; - - [TearDown] - public void Teardown() - { - _snapshotService?.Dispose(); - } - - private void Init() - { - var factory = Mock.Of(); - Current.Factory = factory; - - var publishedModelFactory = new NoopPublishedModelFactory(); - Mock.Get(factory).Setup(x => x.GetService(typeof(IPublishedModelFactory))).Returns(publishedModelFactory); - Mock.Get(factory).Setup(x => x.GetService(typeof(IPublishedValueFallback))).Returns(new NoopPublishedValueFallback()); - - // create a content node kit - var kit = new ContentNodeKit - { - ContentTypeId = 2, - Node = new ContentNode(1, Guid.NewGuid(), 0, "-1,1", 0, -1, DateTime.Now, 0), - DraftData = new ContentData - { - Name = "It Works2!", - Published = false, - TemplateId = 0, - VersionId = 2, - VersionDate = DateTime.Now, - WriterId = 0, - Properties = new Dictionary { { "prop", new[] - { - new PropertyData { Culture = "", Segment = "", Value = "val2" }, - new PropertyData { Culture = "fr-FR", Segment = "", Value = "val-fr2" }, - new PropertyData { Culture = "en-UK", Segment = "", Value = "val-uk2" }, - new PropertyData { Culture = "dk-DA", Segment = "", Value = "val-da2" }, - new PropertyData { Culture = "de-DE", Segment = "", Value = "val-de2" } - } } }, - CultureInfos = new Dictionary - { - // draft data = everything, and IsDraft indicates what's edited - { "fr-FR", new CultureVariation { Name = "name-fr2", IsDraft = true, Date = new DateTime(2018, 01, 03, 01, 00, 00) } }, - { "en-UK", new CultureVariation { Name = "name-uk2", IsDraft = true, Date = new DateTime(2018, 01, 04, 01, 00, 00) } }, - { "dk-DA", new CultureVariation { Name = "name-da2", IsDraft = true, Date = new DateTime(2018, 01, 05, 01, 00, 00) } }, - { "de-DE", new CultureVariation { Name = "name-de1", IsDraft = false, Date = new DateTime(2018, 01, 02, 01, 00, 00) } } - } - }, - PublishedData = new ContentData - { - Name = "It Works1!", - Published = true, - TemplateId = 0, - VersionId = 1, - VersionDate = DateTime.Now, - WriterId = 0, - Properties = new Dictionary { { "prop", new[] - { - new PropertyData { Culture = "", Segment = "", Value = "val1" }, - new PropertyData { Culture = "fr-FR", Segment = "", Value = "val-fr1" }, - new PropertyData { Culture = "en-UK", Segment = "", Value = "val-uk1" } - } } }, - CultureInfos = new Dictionary - { - // published data = only what's actually published, and IsDraft has to be false - { "fr-FR", new CultureVariation { Name = "name-fr1", IsDraft = false, Date = new DateTime(2018, 01, 01, 01, 00, 00) } }, - { "en-UK", new CultureVariation { Name = "name-uk1", IsDraft = false, Date = new DateTime(2018, 01, 02, 01, 00, 00) } }, - { "de-DE", new CultureVariation { Name = "name-de1", IsDraft = false, Date = new DateTime(2018, 01, 02, 01, 00, 00) } } - } - } - }; - - // create a data source for NuCache - var dataSource = new TestDataSource(kit); - _contentNestedDataSerializerFactory = new JsonContentNestedDataSerializerFactory(); - - var runtime = Mock.Of(); - Mock.Get(runtime).Setup(x => x.Level).Returns(RuntimeLevel.Run); - - var serializer = new ConfigurationEditorJsonSerializer(); - - // create data types, property types and content types - var dataType = new DataType(new VoidEditor("Editor", Mock.Of()), serializer) { Id = 3 }; - - var dataTypes = new[] - { - dataType - }; - - _propertyType = new PropertyType(TestHelper.ShortStringHelper, "Umbraco.Void.Editor", ValueStorageType.Nvarchar) { Alias = "prop", DataTypeId = 3, Variations = ContentVariation.Culture }; - _contentType = new ContentType(TestHelper.ShortStringHelper, -1) { Id = 2, Alias = "alias-ct", Variations = ContentVariation.Culture }; - _contentType.AddPropertyType(_propertyType); - - var contentTypes = new[] - { - _contentType - }; - - var contentTypeService = new Mock(); - contentTypeService.Setup(x => x.GetAll()).Returns(contentTypes); - contentTypeService.Setup(x => x.GetAll(It.IsAny())).Returns(contentTypes); - - var mediaTypeService = new Mock(); - mediaTypeService.Setup(x => x.GetAll()).Returns(Enumerable.Empty()); - mediaTypeService.Setup(x => x.GetAll(It.IsAny())).Returns(Enumerable.Empty()); - - var contentTypeServiceBaseFactory = new Mock(); - contentTypeServiceBaseFactory.Setup(x => x.For(It.IsAny())).Returns(contentTypeService.Object); - - var dataTypeService = Mock.Of(); - Mock.Get(dataTypeService).Setup(x => x.GetAll()).Returns(dataTypes); - - // create a service context - var serviceContext = ServiceContext.CreatePartial( - dataTypeService: dataTypeService, - memberTypeService: Mock.Of(), - memberService: Mock.Of(), - contentTypeService: contentTypeService.Object, - mediaTypeService: mediaTypeService.Object, - localizationService: Mock.Of(), - domainService: Mock.Of() - ); - - // create a scope provider - var scopeProvider = Mock.Of(); - Mock.Get(scopeProvider) - .Setup(x => x.CreateScope( - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny(), - It.IsAny())) - .Returns(Mock.Of); - - // create a published content type factory - var contentTypeFactory = new PublishedContentTypeFactory( - publishedModelFactory, - new PropertyValueConverterCollection(Array.Empty()), - dataTypeService); - - // create a variation accessor - _variationAccesor = new TestVariationContextAccessor(); - - var typeFinder = TestHelper.GetTypeFinder(); - - var globalSettings = new GlobalSettings(); - var nuCacheSettings = new NuCacheSettings(); - - // at last, create the complete NuCache snapshot service! - var options = new PublishedSnapshotServiceOptions { IgnoreLocalDb = true }; - _snapshotService = new PublishedSnapshotService( - options, - null, - serviceContext, - contentTypeFactory, - new TestPublishedSnapshotAccessor(), - _variationAccesor, - Mock.Of(), - NullLoggerFactory.Instance, - scopeProvider, - dataSource, - new TestDefaultCultureAccessor(), - Microsoft.Extensions.Options.Options.Create(globalSettings), - Mock.Of(), - publishedModelFactory, - TestHelper.GetHostingEnvironment(), - Microsoft.Extensions.Options.Options.Create(nuCacheSettings), - _contentNestedDataSerializerFactory); - - // invariant is the current default - _variationAccesor.VariationContext = new VariationContext(); - - Mock.Get(factory).Setup(x => x.GetService(typeof(IVariationContextAccessor))).Returns(_variationAccesor); - } - - [Test] - public void StandaloneVariations() - { - // this test implements a full standalone NuCache (based upon a test IDataSource, does not - // use any local db files, does not rely on any database) - and tests variations - - Init(); - - // get a snapshot, get a published content - var snapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - var publishedContent = snapshot.Content.GetById(1); - - Assert.IsNotNull(publishedContent); - Assert.AreEqual("val1", publishedContent.Value(Mock.Of(), "prop")); - Assert.AreEqual("val-fr1", publishedContent.Value(Mock.Of(), "prop", "fr-FR")); - Assert.AreEqual("val-uk1", publishedContent.Value(Mock.Of(), "prop", "en-UK")); - - Assert.IsNull(publishedContent.Name(_variationAccesor)); // no invariant name for varying content - Assert.AreEqual("name-fr1", publishedContent.Name(_variationAccesor, "fr-FR")); - Assert.AreEqual("name-uk1", publishedContent.Name(_variationAccesor, "en-UK")); - - var draftContent = snapshot.Content.GetById(true, 1); - Assert.AreEqual("val2", draftContent.Value(Mock.Of(), "prop")); - Assert.AreEqual("val-fr2", draftContent.Value(Mock.Of(), "prop", "fr-FR")); - Assert.AreEqual("val-uk2", draftContent.Value(Mock.Of(), "prop", "en-UK")); - - Assert.IsNull(draftContent.Name(_variationAccesor)); // no invariant name for varying content - Assert.AreEqual("name-fr2", draftContent.Name(_variationAccesor, "fr-FR")); - Assert.AreEqual("name-uk2", draftContent.Name(_variationAccesor, "en-UK")); - - // now french is default - _variationAccesor.VariationContext = new VariationContext("fr-FR"); - Assert.AreEqual("val-fr1", publishedContent.Value(Mock.Of(), "prop")); - Assert.AreEqual("name-fr1", publishedContent.Name(_variationAccesor)); - Assert.AreEqual(new DateTime(2018, 01, 01, 01, 00, 00), publishedContent.CultureDate(_variationAccesor)); - - // now uk is default - _variationAccesor.VariationContext = new VariationContext("en-UK"); - Assert.AreEqual("val-uk1", publishedContent.Value(Mock.Of(), "prop")); - Assert.AreEqual("name-uk1", publishedContent.Name(_variationAccesor)); - Assert.AreEqual(new DateTime(2018, 01, 02, 01, 00, 00), publishedContent.CultureDate(_variationAccesor)); - - // invariant needs to be retrieved explicitly, when it's not default - Assert.AreEqual("val1", publishedContent.Value(Mock.Of(), "prop", culture: "")); - - // but, - // if the content type / property type does not vary, then it's all invariant again - // modify the content type and property type, notify the snapshot service - _contentType.Variations = ContentVariation.Nothing; - _propertyType.Variations = ContentVariation.Nothing; - _snapshotService.Notify(new[] { new ContentTypeCacheRefresher.JsonPayload("IContentType", publishedContent.ContentType.Id, ContentTypeChangeTypes.RefreshMain) }); - - // get a new snapshot (nothing changed in the old one), get the published content again - var anotherSnapshot = _snapshotService.CreatePublishedSnapshot(previewToken: null); - var againContent = anotherSnapshot.Content.GetById(1); - - Assert.AreEqual(ContentVariation.Nothing, againContent.ContentType.Variations); - Assert.AreEqual(ContentVariation.Nothing, againContent.ContentType.GetPropertyType("prop").Variations); - - // now, "no culture" means "invariant" - Assert.AreEqual("It Works1!", againContent.Name(_variationAccesor)); - Assert.AreEqual("val1", againContent.Value(Mock.Of(), "prop")); - } - - [Test] - public void IsDraftIsPublished() - { - Init(); - - // get the published published content - var s = _snapshotService.CreatePublishedSnapshot(null); - var c1 = s.Content.GetById(1); - - // published content = nothing is draft here - Assert.IsFalse(c1.IsDraft("fr-FR")); - Assert.IsFalse(c1.IsDraft("en-UK")); - Assert.IsFalse(c1.IsDraft("dk-DA")); - Assert.IsFalse(c1.IsDraft("de-DE")); - - // and only those with published name, are published - Assert.IsTrue(c1.IsPublished("fr-FR")); - Assert.IsTrue(c1.IsPublished("en-UK")); - Assert.IsFalse(c1.IsDraft("dk-DA")); - Assert.IsTrue(c1.IsPublished("de-DE")); - - // get the draft published content - var c2 = s.Content.GetById(true, 1); - - // draft content = we have drafts - Assert.IsTrue(c2.IsDraft("fr-FR")); - Assert.IsTrue(c2.IsDraft("en-UK")); - Assert.IsTrue(c2.IsDraft("dk-DA")); - Assert.IsFalse(c2.IsDraft("de-DE")); // except for the one that does not - - // and only those with published name, are published - Assert.IsTrue(c2.IsPublished("fr-FR")); - Assert.IsTrue(c2.IsPublished("en-UK")); - Assert.IsFalse(c2.IsPublished("dk-DA")); - Assert.IsTrue(c2.IsPublished("de-DE")); - } - - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs b/tests/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs deleted file mode 100644 index 9140aec22aa9..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/PublishedContentDataTableTests.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Infrastructure.Serialization; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.PublishedContent -{ - /// - /// Unit tests for IPublishedContent and extensions - /// - [TestFixture] - public class PublishedContentDataTableTests : BaseWebTest - { - public override void SetUp() - { - base.SetUp(); - - // need to specify a different callback for testing - PublishedContentExtensions.GetPropertyAliasesAndNames = (contentTypeService, mediaTypeService, memberTypeService, alias) => - { - var userFields = new Dictionary() - { - {"property1", "Property 1"}, - {"property2", "Property 2"} - }; - if (alias == "Child") - { - userFields.Add("property4", "Property 4"); - } - else - { - userFields.Add("property3", "Property 3"); - } - - //ensure the standard fields are there - var allFields = new Dictionary() - { - {"Id", "Id"}, - {"NodeName", "NodeName"}, - {"NodeTypeAlias", "NodeTypeAlias"}, - {"CreateDate", "CreateDate"}, - {"UpdateDate", "UpdateDate"}, - {"CreatorId", "CreatorId"}, - {"WriterId", "WriterId"}, - {"Url", "Url"} - }; - foreach (var f in userFields.Where(f => !allFields.ContainsKey(f.Key))) - { - allFields.Add(f.Key, f.Value); - } - return allFields; - }; - var umbracoContext = GetUmbracoContext("/test"); - - //set the UmbracoContext.Current since the extension methods rely on it - Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; - } - - public override void TearDown() - { - base.TearDown(); - PublishedContentExtensions.GetPropertyAliasesAndNames = null; - } - - [Test] - public void To_DataTable() - { - var doc = GetContent(true, 1); - var dt = doc.ChildrenAsTable(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()); - - Assert.AreEqual(11, dt.Columns.Count); - Assert.AreEqual(3, dt.Rows.Count); - Assert.AreEqual("value4", dt.Rows[0]["Property 1"]); - Assert.AreEqual("value5", dt.Rows[0]["Property 2"]); - Assert.AreEqual("value6", dt.Rows[0]["Property 4"]); - Assert.AreEqual("value7", dt.Rows[1]["Property 1"]); - Assert.AreEqual("value8", dt.Rows[1]["Property 2"]); - Assert.AreEqual("value9", dt.Rows[1]["Property 4"]); - Assert.AreEqual("value10", dt.Rows[2]["Property 1"]); - Assert.AreEqual("value11", dt.Rows[2]["Property 2"]); - Assert.AreEqual("value12", dt.Rows[2]["Property 4"]); - } - - [Test] - public void To_DataTable_With_Filter() - { - var doc = GetContent(true, 1); - //change a doc type alias - var c = (SolidPublishedContent)doc.Children.ElementAt(0); - c.ContentType = new PublishedContentType(Guid.NewGuid(), 22, "DontMatch", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); - - var dt = doc.ChildrenAsTable(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), "Child"); - - Assert.AreEqual(11, dt.Columns.Count); - Assert.AreEqual(2, dt.Rows.Count); - Assert.AreEqual("value7", dt.Rows[0]["Property 1"]); - Assert.AreEqual("value8", dt.Rows[0]["Property 2"]); - Assert.AreEqual("value9", dt.Rows[0]["Property 4"]); - Assert.AreEqual("value10", dt.Rows[1]["Property 1"]); - Assert.AreEqual("value11", dt.Rows[1]["Property 2"]); - Assert.AreEqual("value12", dt.Rows[1]["Property 4"]); - } - - [Test] - public void To_DataTable_No_Rows() - { - var doc = GetContent(false, 1); - var dt = doc.ChildrenAsTable(Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of(), Mock.Of()); - //will return an empty data table - Assert.AreEqual(0, dt.Columns.Count); - Assert.AreEqual(0, dt.Rows.Count); - } - - private IPublishedContent GetContent(bool createChildren, int indexVals) - { - var serializer = new ConfigurationEditorJsonSerializer(); - var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new VoidEditor(DataValueEditorFactory), serializer) { Id = 1 }); - - var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); - var contentTypeAlias = createChildren ? "Parent" : "Child"; - var contentType = new PublishedContentType(Guid.NewGuid(), 22, contentTypeAlias, PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing); - var d = new SolidPublishedContent(contentType) - { - CreateDate = DateTime.Now, - CreatorId = 1, - Id = 3, - SortOrder = 4, - TemplateId = 5, - UpdateDate = DateTime.Now, - Path = "-1,3", - UrlSegment = "home-page", - Name = "Page" + Guid.NewGuid(), - Version = Guid.NewGuid(), - WriterId = 1, - Parent = null, - Level = 1, - Children = new List() - }; - d.Properties = new Collection(new List - { - new RawValueProperty(factory.CreatePropertyType("property1", 1), d, "value" + indexVals), - new RawValueProperty(factory.CreatePropertyType("property2", 1), d, "value" + (indexVals + 1)) - }); - if (createChildren) - { - d.Children = new List() - { - GetContent(false, indexVals + 3), - GetContent(false, indexVals + 6), - GetContent(false, indexVals + 9) - }; - } - - if (!createChildren) - { - //create additional columns, used to test the different columns for child nodes - ((Collection) d.Properties).Add( - new RawValueProperty(factory.CreatePropertyType("property4",1), d, "value" + (indexVals + 2))); - } - else - { - ((Collection) d.Properties).Add( - new RawValueProperty(factory.CreatePropertyType("property3", 1), d, "value" + (indexVals + 2))); - } - - return d; - } - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs b/tests/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs deleted file mode 100644 index 67d861a564fb..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/PublishedContentExtensionTests.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System.Collections.Generic; -using NUnit.Framework; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.Testing; - -namespace Umbraco.Tests.PublishedContent -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class PublishedContentExtensionTests : PublishedContentTestBase - { - private IUmbracoContext _ctx; - private string _xmlContent = ""; - private bool _createContentTypes = true; - private Dictionary _contentTypes; - - protected override string GetXmlContent(int templateId) - { - return _xmlContent; - } - - [Test] - public void IsDocumentType_NonRecursive_ActualType_ReturnsTrue() - { - InitializeInheritedContentTypes(); - - var publishedContent = _ctx.Content.GetById(1100); - Assert.That(publishedContent.IsDocumentType("inherited", false)); - } - - [Test] - public void IsDocumentType_NonRecursive_BaseType_ReturnsFalse() - { - InitializeInheritedContentTypes(); - - var publishedContent = _ctx.Content.GetById(1100); - Assert.That(publishedContent.IsDocumentType("base", false), Is.False); - } - - [Test] - public void IsDocumentType_Recursive_ActualType_ReturnsTrue() - { - InitializeInheritedContentTypes(); - - var publishedContent = _ctx.Content.GetById(1100); - Assert.That(publishedContent.IsDocumentType("inherited", true)); - } - - [Test] - public void IsDocumentType_Recursive_BaseType_ReturnsTrue() - { - InitializeInheritedContentTypes(); - ContentTypesCache.GetPublishedContentTypeByAlias = null; - - var publishedContent = _ctx.Content.GetById(1100); - Assert.That(publishedContent.IsDocumentType("base", true)); - } - - [Test] - public void IsDocumentType_Recursive_InvalidBaseType_ReturnsFalse() - { - InitializeInheritedContentTypes(); - - var publishedContent = _ctx.Content.GetById(1100); - Assert.That(publishedContent.IsDocumentType("invalidbase", true), Is.False); - } - - private void InitializeInheritedContentTypes() - { - _ctx = GetUmbracoContext("/", 1, null, true); - if (_createContentTypes) - { - var contentTypeService = ServiceContext.ContentTypeService; - var baseType = new ContentType(ShortStringHelper, -1) { Alias = "base", Name = "Base" }; - const string contentTypeAlias = "inherited"; - var inheritedType = new ContentType(ShortStringHelper, baseType, contentTypeAlias) { Alias = contentTypeAlias, Name = "Inherited" }; - contentTypeService.Save(baseType); - contentTypeService.Save(inheritedType); - _contentTypes = new Dictionary - { - { baseType.Alias, new PublishedContentType(baseType, null) }, - { inheritedType.Alias, new PublishedContentType(inheritedType, null) } - }; - ContentTypesCache.GetPublishedContentTypeByAlias = alias => _contentTypes[alias]; - _createContentTypes = false; - } - - ContentTypesCache.GetPublishedContentTypeByAlias = alias => _contentTypes[alias]; - - _xmlContent = @" - - -]> - - -"; - } - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs b/tests/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs deleted file mode 100644 index 5793262bf868..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/PublishedContentLanguageVariantTests.cs +++ /dev/null @@ -1,363 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.Testing; -using Current = Umbraco.Web.Composing.Current; - -namespace Umbraco.Tests.PublishedContent -{ - [TestFixture] - [UmbracoTest(TypeLoader = UmbracoTestOptions.TypeLoader.PerFixture)] - public class PublishedContentLanguageVariantTests : PublishedContentSnapshotTestBase - { - protected override void Compose() - { - base.Compose(); - - Builder.Services.AddUnique(GetServiceContext()); - } - - protected ServiceContext GetServiceContext() - { - var serviceContext = TestObjects.GetServiceContextMock(Factory); - MockLocalizationService(serviceContext); - return serviceContext; - } - - private static void MockLocalizationService(ServiceContext serviceContext) - { - // Set up languages. - // Spanish falls back to English and Italian to Spanish (and then to English). - // French has no fall back. - // Danish, Swedish and Norweigan create an invalid loop. - var globalSettings = new GlobalSettings(); - var languages = new List - { - new Language(globalSettings, "en-US") { Id = 1, CultureName = "English", IsDefault = true }, - new Language(globalSettings, "fr") { Id = 2, CultureName = "French" }, - new Language(globalSettings, "es") { Id = 3, CultureName = "Spanish", FallbackLanguageId = 1 }, - new Language(globalSettings, "it") { Id = 4, CultureName = "Italian", FallbackLanguageId = 3 }, - new Language(globalSettings, "de") { Id = 5, CultureName = "German" }, - new Language(globalSettings, "da") { Id = 6, CultureName = "Danish", FallbackLanguageId = 8 }, - new Language(globalSettings, "sv") { Id = 7, CultureName = "Swedish", FallbackLanguageId = 6 }, - new Language(globalSettings, "no") { Id = 8, CultureName = "Norweigan", FallbackLanguageId = 7 }, - new Language(globalSettings, "nl") { Id = 9, CultureName = "Dutch", FallbackLanguageId = 1 } - }; - - var localizationService = Mock.Get(serviceContext.LocalizationService); - localizationService.Setup(x => x.GetAllLanguages()).Returns(languages); - localizationService.Setup(x => x.GetLanguageById(It.IsAny())) - .Returns((int id) => languages.SingleOrDefault(y => y.Id == id)); - localizationService.Setup(x => x.GetLanguageByIsoCode(It.IsAny())) - .Returns((string c) => languages.SingleOrDefault(y => y.IsoCode == c)); - } - - internal override void PopulateCache(PublishedContentTypeFactory factory, SolidPublishedContentCache cache) - { - var prop1Type = factory.CreatePropertyType("prop1", 1, variations: ContentVariation.Culture); - var welcomeType = factory.CreatePropertyType("welcomeText", 1, variations: ContentVariation.Culture); - var welcome2Type = factory.CreatePropertyType("welcomeText2", 1, variations: ContentVariation.Culture); - var nopropType = factory.CreatePropertyType("noprop", 1, variations: ContentVariation.Culture); - - IEnumerable CreatePropertyTypes1(IPublishedContentType contentType) - { - yield return factory.CreatePropertyType(contentType, "prop1", 1, variations: ContentVariation.Culture); - yield return factory.CreatePropertyType(contentType, "welcomeText", 1, variations: ContentVariation.Culture); - yield return factory.CreatePropertyType(contentType, "welcomeText2", 1, variations: ContentVariation.Culture); - yield return factory.CreatePropertyType(contentType, "noprop", 1, variations: ContentVariation.Culture); - } - - var contentType1 = factory.CreateContentType(Guid.NewGuid(), 1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes1); - - IEnumerable CreatePropertyTypes2(IPublishedContentType contentType) - { - yield return factory.CreatePropertyType(contentType, "prop3", 1, variations: ContentVariation.Culture); - } - - var contentType2 = factory.CreateContentType(Guid.NewGuid(), 2, "contentType2", Enumerable.Empty(), CreatePropertyTypes2); - - var prop1 = new SolidPublishedPropertyWithLanguageVariants - { - Alias = "welcomeText", - PropertyType = welcomeType - }; - prop1.SetSourceValue("en-US", "Welcome", true); - prop1.SetValue("en-US", "Welcome", true); - prop1.SetSourceValue("de", "Willkommen"); - prop1.SetValue("de", "Willkommen"); - prop1.SetSourceValue("nl", "Welkom"); - prop1.SetValue("nl", "Welkom"); - - var prop2 = new SolidPublishedPropertyWithLanguageVariants - { - Alias = "welcomeText2", - PropertyType = welcome2Type - }; - prop2.SetSourceValue("en-US", "Welcome", true); - prop2.SetValue("en-US", "Welcome", true); - - var prop3 = new SolidPublishedPropertyWithLanguageVariants - { - Alias = "welcomeText", - PropertyType = welcomeType - }; - prop3.SetSourceValue("en-US", "Welcome", true); - prop3.SetValue("en-US", "Welcome", true); - - var noprop = new SolidPublishedProperty - { - Alias = "noprop", - PropertyType = nopropType - }; - noprop.SolidHasValue = false; // has no value - noprop.SolidValue = "xxx"; // but returns something - - var item1 = new SolidPublishedContent(contentType1) - { - Id = 1, - SortOrder = 0, - Name = "Content 1", - UrlSegment = "content-1", - Path = "/1", - Level = 1, - ParentId = -1, - ChildIds = new[] { 2 }, - Properties = new Collection - { - prop1, prop2, noprop - } - }; - - var item2 = new SolidPublishedContent(contentType1) - { - Id = 2, - SortOrder = 0, - Name = "Content 2", - UrlSegment = "content-2", - Path = "/1/2", - Level = 2, - ParentId = 1, - ChildIds = new int[] { 3 }, - Properties = new Collection - { - prop3 - } - }; - - var prop4 = new SolidPublishedPropertyWithLanguageVariants - { - Alias = "prop3", - PropertyType = contentType2.GetPropertyType("prop3") - }; - prop4.SetSourceValue("en-US", "Oxxo", true); - prop4.SetValue("en-US", "Oxxo", true); - - var item3 = new SolidPublishedContent(contentType2) - { - Id = 3, - SortOrder = 0, - Name = "Content 3", - UrlSegment = "content-3", - Path = "/1/2/3", - Level = 3, - ParentId = 2, - ChildIds = new int[] { }, - Properties = new Collection - { - prop4 - } - }; - - item1.Children = new List { item2 }; - item2.Parent = item1; - - item2.Children = new List { item3 }; - item3.Parent = item2; - - cache.Add(item1); - cache.Add(item2); - cache.Add(item3); - } - - [Test] - public void Can_Get_Content_For_Populated_Requested_Language() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First(); - var value = content.Value(Mock.Of(), "welcomeText", "en-US"); - Assert.AreEqual("Welcome", value); - } - - [Test] - public void Can_Get_Content_For_Populated_Requested_Non_Default_Language() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First(); - var value = content.Value(Mock.Of(), "welcomeText", "de"); - Assert.AreEqual("Willkommen", value); - } - - [Test] - public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_Without_Fallback() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First(); - var value = content.Value(Mock.Of(), "welcomeText", "fr"); - Assert.IsNull(value); - } - - [Test] - public void Do_Not_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Unless_Requested() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First(); - var value = content.Value(Mock.Of(), "welcomeText", "es"); - Assert.IsNull(value); - } - - [Test] - public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First(); - var value = content.Value(Factory.GetRequiredService(), "welcomeText", "es", fallback: Fallback.ToLanguage); - Assert.AreEqual("Welcome", value); - } - - [Test] - public void Can_Get_Content_For_Unpopulated_Requested_Language_With_Fallback_Over_Two_Levels() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First(); - var value = content.Value(Factory.GetRequiredService(), "welcomeText", "it", fallback: Fallback.To(Fallback.Language, Fallback.Ancestors)); - Assert.AreEqual("Welcome", value); - } - - [Test] - public void Do_Not_GetContent_For_Unpopulated_Requested_Language_With_Fallback_Over_That_Loops() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First(); - var value = content.Value(Mock.Of(), "welcomeText", "no", fallback: Fallback.ToLanguage); - Assert.IsNull(value); - } - - [Test] - public void Do_Not_Get_Content_Recursively_Unless_Requested() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); - var value = content.Value(Mock.Of(), "welcomeText2"); - Assert.IsNull(value); - } - - [Test] - public void Can_Get_Content_Recursively() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); - var value = content.Value(Factory.GetRequiredService(), "welcomeText2", fallback: Fallback.ToAncestors); - Assert.AreEqual("Welcome", value); - } - - [Test] - public void Do_Not_Get_Content_Recursively_Unless_Requested2() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First().Children.First(); - Assert.IsNull(content.GetProperty("welcomeText2")); - var value = content.Value(Mock.Of(), "welcomeText2"); - Assert.IsNull(value); - } - - [Test] - public void Can_Get_Content_Recursively2() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First().Children.First(); - Assert.IsNull(content.GetProperty("welcomeText2")); - var value = content.Value(Factory.GetRequiredService(), "welcomeText2", fallback: Fallback.ToAncestors); - Assert.AreEqual("Welcome", value); - } - - [Test] - public void Can_Get_Content_Recursively3() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First().Children.First(); - Assert.IsNull(content.GetProperty("noprop")); - var value = content.Value(Factory.GetRequiredService(), "noprop", fallback: Fallback.ToAncestors); - // property has no value but we still get the value (ie, the converter would do something) - Assert.AreEqual("xxx", value); - } - - [Test] - public void Can_Get_Content_With_Recursive_Priority() - { - Current.VariationContextAccessor.VariationContext = new VariationContext("nl"); - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); - - var value = content.Value(Factory.GetRequiredService(), "welcomeText", "nl", fallback: Fallback.To(Fallback.Ancestors, Fallback.Language)); - - // No Dutch value is directly assigned. Check has fallen back to Dutch value from parent. - Assert.AreEqual("Welkom", value); - } - - [Test] - public void Can_Get_Content_With_Fallback_Language_Priority() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); - var value = content.Value(Factory.GetRequiredService(), "welcomeText", "nl", fallback: Fallback.ToLanguage); - - // No Dutch value is directly assigned. Check has fallen back to English value from language variant. - Assert.AreEqual("Welcome", value); - } - - [Test] - public void Throws_For_Non_Supported_Fallback() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); - Assert.Throws(() => content.Value(Factory.GetRequiredService(), "welcomeText", "nl", fallback: Fallback.To(999))); - } - - [Test] - public void Can_Fallback_To_Default_Value() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); - - // no Dutch value is assigned, so getting null - var value = content.Value(Factory.GetRequiredService(), "welcomeText", "nl"); - Assert.IsNull(value); - - // even if we 'just' provide a default value - value = content.Value(Factory.GetRequiredService(), "welcomeText", "nl", defaultValue: "woop"); - Assert.IsNull(value); - - // but it works with proper fallback settings - value = content.Value(Factory.GetRequiredService(), "welcomeText", "nl", fallback: Fallback.ToDefaultValue, defaultValue: "woop"); - Assert.AreEqual("woop", value); - } - - [Test] - public void Can_Have_Custom_Default_Value() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First().Children.First(); - - // HACK: the value, pretend the converter would return something - var prop = content.GetProperty("welcomeText") as SolidPublishedPropertyWithLanguageVariants; - Assert.IsNotNull(prop); - prop.SetValue("nl", "nope"); // HasValue false but getting value returns this - - // there is an EN value - var value = content.Value(Factory.GetRequiredService(), "welcomeText", "en-US"); - Assert.AreEqual("Welcome", value); - - // there is no NL value and we get the 'converted' value - value = content.Value(Factory.GetRequiredService(), "welcomeText", "nl"); - Assert.AreEqual("nope", value); - - // but it works with proper fallback settings - value = content.Value(Factory.GetRequiredService(), "welcomeText", "nl", fallback: Fallback.ToDefaultValue, defaultValue: "woop"); - Assert.AreEqual("woop", value); - } - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs b/tests/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs deleted file mode 100644 index b592e9630d97..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using Examine; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Infrastructure; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.Testing; -using Umbraco.Web; -using Umbraco.Web.Composing; - -namespace Umbraco.Tests.PublishedContent -{ - [TestFixture] - [UmbracoTest(TypeLoader = UmbracoTestOptions.TypeLoader.PerFixture)] - public class PublishedContentMoreTests : PublishedContentSnapshotTestBase - { - internal override void PopulateCache(PublishedContentTypeFactory factory, SolidPublishedContentCache cache) - { - IEnumerable CreatePropertyTypes(IPublishedContentType contentType) - { - yield return factory.CreatePropertyType(contentType, "prop1", 1); - } - - var contentType1 = factory.CreateContentType(Guid.NewGuid(), 1, "ContentType1", Enumerable.Empty(), CreatePropertyTypes); - var contentType2 = factory.CreateContentType(Guid.NewGuid(), 2, "ContentType2", Enumerable.Empty(), CreatePropertyTypes); - var contentType2Sub = factory.CreateContentType(Guid.NewGuid(), 3, "ContentType2Sub", Enumerable.Empty(), CreatePropertyTypes); - - var content = new SolidPublishedContent(contentType1) - { - Id = 1, - SortOrder = 0, - Name = "Content 1", - UrlSegment = "content-1", - Path = "/1", - Level = 1, - ParentId = -1, - ChildIds = new int[] { }, - Properties = new Collection - { - new SolidPublishedProperty - { - Alias = "prop1", - SolidHasValue = true, - SolidValue = 1234, - SolidSourceValue = "1234" - } - } - }; - cache.Add(content); - - content = new SolidPublishedContent(contentType2) - { - Id = 2, - SortOrder = 1, - Name = "Content 2", - UrlSegment = "content-2", - Path = "/2", - Level = 1, - ParentId = -1, - ChildIds = new int[] { }, - Properties = new Collection - { - new SolidPublishedProperty - { - Alias = "prop1", - SolidHasValue = true, - SolidValue = 1234, - SolidSourceValue = "1234" - } - } - }; - cache.Add(content); - - content = new SolidPublishedContent(contentType2Sub) - { - Id = 3, - SortOrder = 2, - Name = "Content 2Sub", - UrlSegment = "content-2sub", - Path = "/3", - Level = 1, - ParentId = -1, - ChildIds = new int[] { }, - Properties = new Collection - { - new SolidPublishedProperty - { - Alias = "prop1", - SolidHasValue = true, - SolidValue = 1234, - SolidSourceValue = "1234" - } - } - }; - cache.Add(content); - } - - [Test] - public void First() - { - var content = Current.UmbracoContext.Content.GetAtRoot().First(); - Assert.AreEqual("Content 1", content.Name(VariationContextAccessor)); - } - - [Test] - public void Distinct() - { - var items = Current.UmbracoContext.Content.GetAtRoot() - .Distinct() - .Distinct() - .ToIndexedArray(); - - var item = items[0]; - Assert.AreEqual("Content 1", item.Content.Name); - Assert.IsTrue(item.IsFirst()); - Assert.IsFalse(item.IsLast()); - - item = items[1]; - Assert.AreEqual("Content 2", item.Content.Name); - Assert.IsFalse(item.IsFirst()); - Assert.IsFalse(item.IsLast()); - - item = items[2]; - Assert.AreEqual("Content 2Sub", item.Content.Name); - Assert.IsFalse(item.IsFirst()); - Assert.IsTrue(item.IsLast()); - } - - [Test] - public void OfType1() - { - var items = Current.UmbracoContext.Content.GetAtRoot() - .OfType() - .Distinct() - .ToIndexedArray(); - Assert.AreEqual(2, items.Length); - Assert.IsInstanceOf(items.First().Content); - } - - [Test] - public void OfType2() - { - var content = Current.UmbracoContext.Content.GetAtRoot() - .OfType() - .Distinct() - .ToIndexedArray(); - Assert.AreEqual(1, content.Length); - Assert.IsInstanceOf(content.First().Content); - } - - [Test] - public void OfType() - { - var content = Current.UmbracoContext.Content.GetAtRoot() - .OfType() - .First(x => x.Prop1 == 1234); - Assert.AreEqual("Content 2", content.Name); - Assert.AreEqual(1234, content.Prop1); - } - - [Test] - public void Position() - { - var items = Current.UmbracoContext.Content.GetAtRoot() - .Where(x => x.Value(Mock.Of(), "prop1") == 1234) - .ToIndexedArray(); - - Assert.IsTrue(items.First().IsFirst()); - Assert.IsFalse(items.First().IsLast()); - Assert.IsFalse(items.Skip(1).First().IsFirst()); - Assert.IsFalse(items.Skip(1).First().IsLast()); - Assert.IsFalse(items.Skip(2).First().IsFirst()); - Assert.IsTrue(items.Skip(2).First().IsLast()); - } - - [Test] - public void Issue() - { - var content = Current.UmbracoContext.Content.GetAtRoot() - .Distinct() - .OfType(); - - var where = content.Where(x => x.Prop1 == 1234); - var first = where.First(); - Assert.AreEqual(1234, first.Prop1); - - var content2 = Current.UmbracoContext.Content.GetAtRoot() - .OfType() - .First(x => x.Prop1 == 1234); - Assert.AreEqual(1234, content2.Prop1); - - var content3 = Current.UmbracoContext.Content.GetAtRoot() - .OfType() - .First(); - Assert.AreEqual(1234, content3.Prop1); - } - - [Test] - public void PublishedContentQueryTypedContentList() - { - var examineManager = new Mock(); - var query = new PublishedContentQuery(Current.UmbracoContext.PublishedSnapshot, Current.UmbracoContext.VariationContextAccessor, examineManager.Object); - var result = query.Content(new[] { 1, 2, 4 }).ToArray(); - Assert.AreEqual(2, result.Length); - Assert.AreEqual(1, result[0].Id); - Assert.AreEqual(2, result[1].Id); - } - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs b/tests/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs deleted file mode 100644 index e0b95f95e453..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/PublishedContentSnapshotTestBase.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Web.Routing; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Serialization; -using Umbraco.Cms.Tests.Common; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web; - -namespace Umbraco.Tests.PublishedContent -{ - public abstract class PublishedContentSnapshotTestBase : PublishedContentTestBase - { - // read http://stackoverflow.com/questions/7713326/extension-method-that-works-on-ienumerablet-and-iqueryablet - // and http://msmvps.com/blogs/jon_skeet/archive/2010/10/28/overloading-and-generic-constraints.aspx - // and http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx - - public override void SetUp() - { - base.SetUp(); - - var umbracoContext = GetUmbracoContext(); - Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; - } - - protected override void Compose() - { - base.Compose(); - - Builder.Services.AddUnique(f => new PublishedModelFactory(f.GetRequiredService().GetTypes(), f.GetRequiredService())); - } - - protected override TypeLoader CreateTypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, ILogger logger, IProfilingLogger profilingLogger , IHostingEnvironment hostingEnvironment) - { - var baseLoader = base.CreateTypeLoader(ioHelper, typeFinder, runtimeCache, logger, profilingLogger , hostingEnvironment); - - return new TypeLoader(typeFinder, runtimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), logger, profilingLogger , false, - // this is so the model factory looks into the test assembly - baseLoader.AssembliesToScan - .Union(new[] {typeof(PublishedContentMoreTests).Assembly}) - .ToList()); - } - - private IUmbracoContext GetUmbracoContext() - { - RouteData routeData = null; - - var publishedSnapshot = CreatePublishedSnapshot(); - - var publishedSnapshotService = new Mock(); - publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot); - - var globalSettings = TestObjects.GetGlobalSettings(); - - var httpContext = GetHttpContextFactory("http://umbraco.local/", routeData).HttpContext; - - var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext); - var umbracoContext = new UmbracoContext( - httpContextAccessor, - publishedSnapshotService.Object, - Mock.Of(), - globalSettings, - HostingEnvironment, - new TestVariationContextAccessor(), - UriUtility, - new AspNetCookieManager(httpContextAccessor)); - - return umbracoContext; - } - - private SolidPublishedSnapshot CreatePublishedSnapshot() - { - var serializer = new ConfigurationEditorJsonSerializer(); - var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new VoidEditor(DataValueEditorFactory), serializer) { Id = 1 }); - - var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); - var caches = new SolidPublishedSnapshot(); - var cache = caches.InnerContentCache; - PopulateCache(factory, cache); - return caches; - } - - internal abstract void PopulateCache(PublishedContentTypeFactory factory, SolidPublishedContentCache cache); - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs b/tests/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs deleted file mode 100644 index 7befa03f45f0..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/PublishedContentTestBase.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Media; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.PropertyEditors.ValueConverters; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Core.Templates; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Serialization; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.PublishedContent -{ - /// - /// Abstract base class for tests for published content and published media - /// - public abstract class PublishedContentTestBase : BaseWebTest - { - protected override void Compose() - { - base.Compose(); - - // FIXME: what about the if (PropertyValueConvertersResolver.HasCurrent == false) ?? - // can we risk double - registering and then, what happens? - - Builder.WithCollectionBuilder() - .Clear() - .Append() - .Append() - .Append(); - } - - protected override void Initialize() - { - base.Initialize(); - - var converters = Factory.GetRequiredService(); - var umbracoContextAccessor = Mock.Of(); - var publishedUrlProvider = Mock.Of(); - var loggerFactory = NullLoggerFactory.Instance; - var serializer = new ConfigurationEditorJsonSerializer(); - - var imageSourceParser = new HtmlImageSourceParser(publishedUrlProvider); - var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), - loggerFactory.CreateLogger(), Mock.Of()); - var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, loggerFactory.CreateLogger(), HostingEnvironment, Mock.Of(), Mock.Of(), mediaFileManager, ShortStringHelper, publishedUrlProvider, serializer); - var localLinkParser = new HtmlLocalLinkParser(umbracoContextAccessor, publishedUrlProvider); - var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new RichTextPropertyEditor( - DataValueEditorFactory, - Mock.Of(), - imageSourceParser, - localLinkParser, - pastedImages, - IOHelper, - Mock.Of()), - serializer) { Id = 1 }); - - - var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of(), converters, dataTypeService); - - IEnumerable CreatePropertyTypes(IPublishedContentType contentType) - { - yield return publishedContentTypeFactory.CreatePropertyType(contentType, "content", 1); - } - - var type = new AutoPublishedContentType(Guid.NewGuid(), 0, "anything", CreatePropertyTypes); - ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; - - var umbracoContext = GetUmbracoContext("/test"); - Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; - } - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/tests/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs deleted file mode 100644 index 15a867a7d8ca..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ /dev/null @@ -1,511 +0,0 @@ -using System.Linq; -using System.Threading; -using System.Xml; -using System.Xml.Linq; -using System.Xml.XPath; -using Microsoft.Extensions.DependencyInjection; -using Examine; -using NUnit.Framework; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.Membership; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Examine; -using Umbraco.Cms.Tests.Common; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.LegacyXmlPublishedCache; -using Umbraco.Tests.TestHelpers.Entities; - - -namespace Umbraco.Tests.PublishedContent -{ - /// - /// Tests the typed extension methods on IPublishedContent using the DefaultPublishedMediaStore - /// - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, WithApplication = true)] - public class PublishedMediaTests : PublishedContentTestBase - { - /// - /// sets up resolvers before resolution is frozen - /// - protected override void Compose() - { - base.Compose(); - - Builder.WithCollectionBuilder() - .Clear() - .Append(); - - Builder.Services.AddUnique(); - } - - private IMediaType MakeNewMediaType(IUser user, string text, int parentId = -1) - { - var mt = new MediaType(ShortStringHelper, parentId) { Name = text, Alias = text, Thumbnail = "icon-folder", Icon = "icon-folder" }; - ServiceContext.MediaTypeService.Save(mt); - return mt; - } - - private IMedia MakeNewMedia(string name, IMediaType mediaType, IUser user, int parentId) - { - var m = ServiceContext.MediaService.CreateMediaWithIdentity(name, parentId, mediaType.Alias); - return m; - } - - /// - /// Shared with PublishMediaStoreTests - /// - /// - /// - /// - internal IPublishedContent GetNode(int id, IUmbracoContext umbracoContext) - { - var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), - ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, - Factory.GetRequiredService(), Factory.GetRequiredService(), VariationContextAccessor); - var doc = cache.GetById(id); - Assert.IsNotNull(doc); - return doc; - } - - private IPublishedContent GetNode(int id) - { - return GetNode(id, GetUmbracoContext("/test")); - } - - [Test] - public void Get_Property_Value_Uses_Converter() - { - var mType = MockedContentTypes.CreateImageMediaType("image2"); - //lets add an RTE to this - mType.PropertyGroups.First().PropertyTypes.Add( - new PropertyType(ShortStringHelper, "test", ValueStorageType.Nvarchar, "content") - { - Name = "Rich Text", - DataTypeId = -87 //tiny mce - }); - var existing = ServiceContext.MediaTypeService.GetAll(); - ServiceContext.MediaTypeService.Save(mType); - var media = MockedMedia.CreateMediaImage(mType, -1); - media.Properties["content"].SetValue("
This is some content
"); - ServiceContext.MediaService.Save(media); - - var publishedMedia = GetNode(media.Id); - - var propVal = publishedMedia.Value(Factory.GetRequiredService(), "content"); - Assert.IsInstanceOf(propVal); - Assert.AreEqual("
This is some content
", propVal.ToString()); - - var propVal2 = publishedMedia.Value(Factory.GetRequiredService(), "content"); - Assert.IsInstanceOf(propVal2); - Assert.AreEqual("
This is some content
", propVal2.ToString()); - - var propVal3 = publishedMedia.Value(Factory.GetRequiredService(), "Content"); - Assert.IsInstanceOf(propVal3); - Assert.AreEqual("
This is some content
", propVal3.ToString()); - } - - [Test] - public void Ensure_Children_Sorted_With_Examine() - { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockMediaService()); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(true))) - using (indexer.ProcessNonAsync()) - { - rebuilder.RegisterIndex(indexer.Name); - rebuilder.Populate(indexer); - - var searcher = indexer.GetSearcher(); - var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService()); - - //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace - var publishedMedia = cache.GetById(1111); - var rootChildren = publishedMedia.Children(VariationContextAccessor).ToArray(); - var currSort = 0; - for (var i = 0; i < rootChildren.Count(); i++) - { - Assert.GreaterOrEqual(rootChildren[i].SortOrder, currSort); - currSort = rootChildren[i].SortOrder; - } - } - } - - [Test] - public void Do_Not_Find_In_Recycle_Bin() - { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockMediaService()); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - //include unpublished content since this uses the 'internal' indexer, it's up to the media cache to filter - validator: new ContentValueSetValidator(false))) - using (indexer.ProcessNonAsync()) - { - rebuilder.RegisterIndex(indexer.Name); - rebuilder.Populate(indexer); - - var searcher = indexer.GetSearcher(); - var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService()); - - //ensure it is found - var publishedMedia = cache.GetById(3113); - Assert.IsNotNull(publishedMedia); - - //move item to recycle bin - var newXml = XElement.Parse(@" - - 115 - 268 - 10726 - jpg - "); - indexer.IndexItems(new[]{ newXml.ConvertToValueSet("media") }); - - - //ensure it still exists in the index (raw examine search) - var criteria = searcher.CreateQuery(); - var filter = criteria.Id(3113); - var found = filter.Execute(); - Assert.IsNotNull(found); - Assert.AreEqual(1, found.TotalItemCount); - - //ensure it does not show up in the published media store - var recycledMedia = cache.GetById(3113); - Assert.IsNull(recycledMedia); - - } - - } - - [Test] - public void Children_With_Examine() - { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockMediaService()); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(true))) - using (indexer.ProcessNonAsync()) - { - rebuilder.RegisterIndex(indexer.Name); - rebuilder.Populate(indexer); - - var searcher = indexer.GetSearcher(); - var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService()); - - //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace - var publishedMedia = cache.GetById(1111); - var rootChildren = publishedMedia.Children(VariationContextAccessor); - Assert.IsTrue(rootChildren.Select(x => x.Id).ContainsAll(new[] { 2222, 1113, 1114, 1115, 1116 })); - - var publishedChild1 = cache.GetById(2222); - var subChildren = publishedChild1.Children(VariationContextAccessor); - Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(new[] { 2112 })); - } - } - - [Test] - public void Descendants_With_Examine() - { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockMediaService()); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(true))) - using (indexer.ProcessNonAsync()) - { - rebuilder.RegisterIndex(indexer.Name); - rebuilder.Populate(indexer); - - var searcher = indexer.GetSearcher(); - var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService()); - - //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace - var publishedMedia = cache.GetById(1111); - var rootDescendants = publishedMedia.Descendants(Factory.GetRequiredService()); - Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(new[] { 2112, 2222, 1113, 1114, 1115, 1116 })); - - var publishedChild1 = cache.GetById(2222); - var subDescendants = publishedChild1.Descendants(Factory.GetRequiredService()); - Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { 2112, 3113 })); - } - } - - [Test] - public void DescendantsOrSelf_With_Examine() - { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockMediaService()); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(true))) - using (indexer.ProcessNonAsync()) - { - rebuilder.RegisterIndex(indexer.Name); - rebuilder.Populate(indexer); - - var searcher = indexer.GetSearcher(); - var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService()); - - //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace - var publishedMedia = cache.GetById(1111); - var rootDescendants = publishedMedia.DescendantsOrSelf(Factory.GetRequiredService()); - Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(new[] { 1111, 2112, 2222, 1113, 1114, 1115, 1116 })); - - var publishedChild1 = cache.GetById(2222); - var subDescendants = publishedChild1.DescendantsOrSelf(Factory.GetRequiredService()); - Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { 2222, 2112, 3113 })); - } - } - - [Test] - public void Ancestors_With_Examine() - { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockMediaService()); - - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(true))) - using (indexer.ProcessNonAsync()) - { - rebuilder.RegisterIndex(indexer.Name); - rebuilder.Populate(indexer); - - var ctx = GetUmbracoContext("/test"); - var searcher = indexer.GetSearcher(); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService()); - - //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace - var publishedMedia = cache.GetById(3113); - var ancestors = publishedMedia.Ancestors(); - Assert.IsTrue(ancestors.Select(x => x.Id).ContainsAll(new[] { 2112, 2222, 1111 })); - } - - } - - [Test] - public void AncestorsOrSelf_With_Examine() - { - var rebuilder = IndexInitializer.GetMediaIndexRebuilder(Factory.GetRequiredService(), IndexInitializer.GetMockMediaService()); - - using (var luceneDir = new RandomIdRamDirectory()) - using (var indexer = IndexInitializer.GetUmbracoIndexer(ProfilingLogger, HostingEnvironment, RuntimeState, luceneDir, - validator: new ContentValueSetValidator(true))) - using (indexer.ProcessNonAsync()) - { - rebuilder.RegisterIndex(indexer.Name); - rebuilder.Populate(indexer); - - - var ctx = GetUmbracoContext("/test"); - var searcher = indexer.GetSearcher(); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService()); - - //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace - var publishedMedia = cache.GetById(3113); - var ancestors = publishedMedia.AncestorsOrSelf(); - Assert.IsTrue(ancestors.Select(x => x.Id).ContainsAll(new[] { 3113, 2112, 2222, 1111 })); - } - } - - [Test] - public void Children_Without_Examine() - { - var user = ServiceContext.UserService.GetUserById(0); - var mType = MakeNewMediaType(user, "TestMediaType"); - var mRoot = MakeNewMedia("MediaRoot", mType, user, -1); - - var mChild1 = MakeNewMedia("Child1", mType, user, mRoot.Id); - var mChild2 = MakeNewMedia("Child2", mType, user, mRoot.Id); - var mChild3 = MakeNewMedia("Child3", mType, user, mRoot.Id); - - var mSubChild1 = MakeNewMedia("SubChild1", mType, user, mChild1.Id); - var mSubChild2 = MakeNewMedia("SubChild2", mType, user, mChild1.Id); - var mSubChild3 = MakeNewMedia("SubChild3", mType, user, mChild1.Id); - - var publishedMedia = GetNode(mRoot.Id); - var rootChildren = publishedMedia.Children(VariationContextAccessor); - Assert.IsTrue(rootChildren.Select(x => x.Id).ContainsAll(new[] { mChild1.Id, mChild2.Id, mChild3.Id })); - - var publishedChild1 = GetNode(mChild1.Id); - var subChildren = publishedChild1.Children(VariationContextAccessor); - Assert.IsTrue(subChildren.Select(x => x.Id).ContainsAll(new[] { mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); - } - - [Test] - public void Descendants_Without_Examine() - { - var user = ServiceContext.UserService.GetUserById(0); - var mType = MakeNewMediaType(user, "TestMediaType"); - var mRoot = MakeNewMedia("MediaRoot", mType, user, -1); - - var mChild1 = MakeNewMedia("Child1", mType, user, mRoot.Id); - var mChild2 = MakeNewMedia("Child2", mType, user, mRoot.Id); - var mChild3 = MakeNewMedia("Child3", mType, user, mRoot.Id); - - var mSubChild1 = MakeNewMedia("SubChild1", mType, user, mChild1.Id); - var mSubChild2 = MakeNewMedia("SubChild2", mType, user, mChild1.Id); - var mSubChild3 = MakeNewMedia("SubChild3", mType, user, mChild1.Id); - - var publishedMedia = GetNode(mRoot.Id); - var rootDescendants = publishedMedia.Descendants(Factory.GetRequiredService()); - Assert.IsTrue(rootDescendants.Select(x => x.Id).ContainsAll(new[] { mChild1.Id, mChild2.Id, mChild3.Id, mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); - - var publishedChild1 = GetNode(mChild1.Id); - var subDescendants = publishedChild1.Descendants(Factory.GetRequiredService()); - Assert.IsTrue(subDescendants.Select(x => x.Id).ContainsAll(new[] { mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); - } - - [Test] - public void DescendantsOrSelf_Without_Examine() - { - var user = ServiceContext.UserService.GetUserById(0); - var mType = MakeNewMediaType(user, "TestMediaType"); - var mRoot = MakeNewMedia("MediaRoot", mType, user, -1); - - var mChild1 = MakeNewMedia("Child1", mType, user, mRoot.Id); - var mChild2 = MakeNewMedia("Child2", mType, user, mRoot.Id); - var mChild3 = MakeNewMedia("Child3", mType, user, mRoot.Id); - - var mSubChild1 = MakeNewMedia("SubChild1", mType, user, mChild1.Id); - var mSubChild2 = MakeNewMedia("SubChild2", mType, user, mChild1.Id); - var mSubChild3 = MakeNewMedia("SubChild3", mType, user, mChild1.Id); - - var publishedMedia = GetNode(mRoot.Id); - var rootDescendantsOrSelf = publishedMedia.DescendantsOrSelf(Factory.GetRequiredService()); - Assert.IsTrue(rootDescendantsOrSelf.Select(x => x.Id).ContainsAll( - new[] { mRoot.Id, mChild1.Id, mChild2.Id, mChild3.Id, mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); - - var publishedChild1 = GetNode(mChild1.Id); - var subDescendantsOrSelf = publishedChild1.DescendantsOrSelf(Factory.GetRequiredService()); - Assert.IsTrue(subDescendantsOrSelf.Select(x => x.Id).ContainsAll( - new[] { mChild1.Id, mSubChild1.Id, mSubChild2.Id, mSubChild3.Id })); - } - - [Test] - public void Parent_Without_Examine() - { - var user = ServiceContext.UserService.GetUserById(0); - var mType = MakeNewMediaType(user, "TestMediaType"); - var mRoot = MakeNewMedia("MediaRoot", mType, user, -1); - - var mChild1 = MakeNewMedia("Child1", mType, user, mRoot.Id); - var mChild2 = MakeNewMedia("Child2", mType, user, mRoot.Id); - var mChild3 = MakeNewMedia("Child3", mType, user, mRoot.Id); - - var mSubChild1 = MakeNewMedia("SubChild1", mType, user, mChild1.Id); - var mSubChild2 = MakeNewMedia("SubChild2", mType, user, mChild1.Id); - var mSubChild3 = MakeNewMedia("SubChild3", mType, user, mChild1.Id); - - var publishedRoot = GetNode(mRoot.Id); - Assert.AreEqual(null, publishedRoot.Parent); - - var publishedChild1 = GetNode(mChild1.Id); - Assert.AreEqual(mRoot.Id, publishedChild1.Parent.Id); - - var publishedSubChild1 = GetNode(mSubChild1.Id); - Assert.AreEqual(mChild1.Id, publishedSubChild1.Parent.Id); - } - - [Test] - public void Ancestors_Without_Examine() - { - var user = ServiceContext.UserService.GetUserById(0); - var mType = MakeNewMediaType(user, "TestMediaType"); - var mRoot = MakeNewMedia("MediaRoot", mType, user, -1); - - var mChild1 = MakeNewMedia("Child1", mType, user, mRoot.Id); - var mChild2 = MakeNewMedia("Child2", mType, user, mRoot.Id); - var mChild3 = MakeNewMedia("Child3", mType, user, mRoot.Id); - - var mSubChild1 = MakeNewMedia("SubChild1", mType, user, mChild1.Id); - var mSubChild2 = MakeNewMedia("SubChild2", mType, user, mChild1.Id); - var mSubChild3 = MakeNewMedia("SubChild3", mType, user, mChild1.Id); - - var publishedSubChild1 = GetNode(mSubChild1.Id); - Assert.IsTrue(publishedSubChild1.Ancestors().Select(x => x.Id).ContainsAll(new[] { mChild1.Id, mRoot.Id })); - } - - [Test] - public void AncestorsOrSelf_Without_Examine() - { - var user = ServiceContext.UserService.GetUserById(0); - var mType = MakeNewMediaType(user, "TestMediaType"); - var mRoot = MakeNewMedia("MediaRoot", mType, user, -1); - - var mChild1 = MakeNewMedia("Child1", mType, user, mRoot.Id); - var mChild2 = MakeNewMedia("Child2", mType, user, mRoot.Id); - var mChild3 = MakeNewMedia("Child3", mType, user, mRoot.Id); - - var mSubChild1 = MakeNewMedia("SubChild1", mType, user, mChild1.Id); - var mSubChild2 = MakeNewMedia("SubChild2", mType, user, mChild1.Id); - var mSubChild3 = MakeNewMedia("SubChild3", mType, user, mChild1.Id); - - var publishedSubChild1 = GetNode(mSubChild1.Id); - Assert.IsTrue(publishedSubChild1.AncestorsOrSelf().Select(x => x.Id).ContainsAll( - new[] { mSubChild1.Id, mChild1.Id, mRoot.Id })); - } - - [Test] - public void Convert_From_Standard_Xml() - { - var nodeId = 2112; - - var xml = XElement.Parse(@" - - 115 - 268 - 10726 - jpg - - - 115 - 268 - 10726 - jpg - - "); - var node = xml.DescendantsAndSelf("Image").Single(x => (int)x.Attribute("id") == nodeId); - - var publishedMedia = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService(), VariationContextAccessor); - - var nav = node.CreateNavigator(); - - var converted = publishedMedia.CreateFromCacheValues( - publishedMedia.ConvertFromXPathNodeIterator(nav.Select("/Image"), nodeId)); - - Assert.AreEqual(nodeId, converted.Id); - Assert.AreEqual(3, converted.Level); - Assert.AreEqual(1, converted.SortOrder); - Assert.AreEqual("Sam's Umbraco Image", converted.Name); - Assert.AreEqual("-1,1111,2222,2112", converted.Path); - } - - [Test] - public void Detects_Error_In_Xml() - { - var errorXml = new XElement("error", string.Format("No media is maching '{0}'", 1234)); - var nav = errorXml.CreateNavigator(); - - var publishedMedia = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null, HostingEnvironment), ServiceContext.MediaService, ServiceContext.UserService, new DictionaryAppCache(), ContentTypesCache, Factory.GetRequiredService(), Factory.GetRequiredService(), VariationContextAccessor); - var converted = publishedMedia.ConvertFromXPathNodeIterator(nav.Select("/"), 1234); - - Assert.IsNull(converted); - } - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs b/tests/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs deleted file mode 100644 index e59043e087f2..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/PublishedRouterTests.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Routing; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.PublishedContent -{ - [TestFixture] - public class PublishedRouterTests : BaseWebTest - { - [Test] - public async Task ConfigureRequest_Returns_False_Without_HasPublishedContent() - { - var umbracoContext = GetUmbracoContext("/test"); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var request = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - var result = publishedRouter.BuildRequest(request); - - Assert.IsFalse(result.Success()); - } - - [Test] - public async Task ConfigureRequest_Returns_False_When_IsRedirect() - { - var umbracoContext = GetUmbracoContext("/test"); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var request = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - var content = GetPublishedContentMock(); - request.SetPublishedContent(content.Object); - request.SetCulture("en-AU"); - request.SetRedirect("/hello"); - var result = publishedRouter.BuildRequest(request); - - Assert.IsFalse(result.Success()); - } - - private Mock GetPublishedContentMock() - { - var pc = new Mock(); - pc.Setup(content => content.Id).Returns(1); - pc.Setup(content => content.Name).Returns("test"); - pc.Setup(content => content.CreateDate).Returns(DateTime.Now); - pc.Setup(content => content.UpdateDate).Returns(DateTime.Now); - pc.Setup(content => content.Path).Returns("-1,1"); - pc.Setup(content => content.Parent).Returns(() => null); - pc.Setup(content => content.Properties).Returns(new Collection()); - pc.Setup(content => content.ContentType).Returns(new PublishedContentType(Guid.NewGuid(), 22, "anything", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing)); - return pc; - } - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/RootNodeTests.cs b/tests/Umbraco.Tests/PublishedContent/RootNodeTests.cs deleted file mode 100644 index 4aad3d0acbd9..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/RootNodeTests.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Linq; -using NUnit.Framework; -using Umbraco.Web; - -namespace Umbraco.Tests.PublishedContent -{ - [TestFixture] - public class RootNodeTests : PublishedContentTestBase - { - [Test] - public void PublishedContentHasNoRootNode() - { - var ctx = GetUmbracoContext("/test"); - - // there is no content node with ID -1 - var content = ctx.Content.GetById(-1); - Assert.IsNull(content); - - // content at root has null parent - content = ctx.Content.GetById(1046); - Assert.IsNotNull(content); - Assert.AreEqual(1, content.Level); - Assert.IsNull(content.Parent); - - // non-existing content is null - content = ctx.Content.GetById(666); - Assert.IsNull(content); - } - - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs b/tests/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs deleted file mode 100644 index 4b2d999ce360..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs +++ /dev/null @@ -1,449 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Core.Xml; -using Umbraco.Cms.Infrastructure.Serialization; -using Umbraco.Extensions; -using Umbraco.Web.Composing; - -namespace Umbraco.Tests.PublishedContent -{ - public class SolidPublishedSnapshot : IPublishedSnapshot - { - public readonly SolidPublishedContentCache InnerContentCache = new SolidPublishedContentCache(); - public readonly SolidPublishedContentCache InnerMediaCache = new SolidPublishedContentCache(); - - public IPublishedContentCache Content => InnerContentCache; - - public IPublishedMediaCache Media => InnerMediaCache; - - public IPublishedMemberCache Members => null; - - public IDomainCache Domains => null; - - public IDisposable ForcedPreview(bool forcedPreview, Action callback = null) - { - throw new NotImplementedException(); - } - - public void Resync() - { } - - public IAppCache SnapshotCache => null; - - public IAppCache ElementsCache => null; - - public void Dispose() - { } - } - - public class SolidPublishedContentCache : PublishedCacheBase, IPublishedContentCache, IPublishedMediaCache - { - private readonly Dictionary _content = new Dictionary(); - - public SolidPublishedContentCache() - : base(false) - { } - - public void Add(SolidPublishedContent content) - { - _content[content.Id] = content.CreateModel(Current.PublishedModelFactory); - } - - public void Clear() - { - _content.Clear(); - } - - public IPublishedContent GetByRoute(bool preview, string route, bool? hideTopLevelNode = null, string culture = null) - { - throw new NotImplementedException(); - } - - public IPublishedContent GetByRoute(string route, bool? hideTopLevelNode = null, string culture = null) - { - throw new NotImplementedException(); - } - - public string GetRouteById(bool preview, int contentId, string culture = null) - { - throw new NotImplementedException(); - } - - public string GetRouteById(int contentId, string culture = null) - { - throw new NotImplementedException(); - } - - public override IPublishedContent GetById(bool preview, int contentId) - { - return _content.ContainsKey(contentId) ? _content[contentId] : null; - } - - public override IPublishedContent GetById(bool preview, Guid contentId) - { - throw new NotImplementedException(); - } - - public override IPublishedContent GetById(bool preview, Udi nodeId) - => throw new NotSupportedException(); - - public override bool HasById(bool preview, int contentId) - { - return _content.ContainsKey(contentId); - } - - public override IEnumerable GetAtRoot(bool preview, string culture = null) - { - return _content.Values.Where(x => x.Parent == null); - } - - public override IPublishedContent GetSingleByXPath(bool preview, string xpath, XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override IPublishedContent GetSingleByXPath(bool preview, System.Xml.XPath.XPathExpression xpath, XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override IEnumerable GetByXPath(bool preview, string xpath, XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override IEnumerable GetByXPath(bool preview, System.Xml.XPath.XPathExpression xpath, XPathVariable[] vars) - { - throw new NotImplementedException(); - } - - public override System.Xml.XPath.XPathNavigator CreateNavigator(bool preview) - { - throw new NotImplementedException(); - } - - public override System.Xml.XPath.XPathNavigator CreateNodeNavigator(int id, bool preview) - { - throw new NotImplementedException(); - } - - public override bool HasContent(bool preview) - { - return _content.Count > 0; - } - - public override IPublishedContentType GetContentType(int id) - { - throw new NotImplementedException(); - } - - public override IPublishedContentType GetContentType(string alias) - { - throw new NotImplementedException(); - } - - public override IPublishedContentType GetContentType(Guid key) - { - throw new NotImplementedException(); - } - - public override IEnumerable GetByContentType(IPublishedContentType contentType) - { - throw new NotImplementedException(); - } - } - - public class SolidPublishedContent : IPublishedContent - { - #region Constructor - - public SolidPublishedContent(IPublishedContentType contentType) - { - // initialize boring stuff - TemplateId = 0; - WriterId = CreatorId = 0; - CreateDate = UpdateDate = DateTime.Now; - Version = Guid.Empty; - - ContentType = contentType; - } - - #endregion - - #region Content - - private Dictionary _cultures; - - private Dictionary GetCultures() - { - return new Dictionary { { "", new PublishedCultureInfo("", Name, UrlSegment, UpdateDate) } }; - } - - public int Id { get; set; } - public Guid Key { get; set; } - public int? TemplateId { get; set; } - public int SortOrder { get; set; } - public string Name { get; set; } - public IReadOnlyDictionary Cultures => _cultures ?? (_cultures = GetCultures()); - public string UrlSegment { get; set; } - public int WriterId { get; set; } - public int CreatorId { get; set; } - public string Path { get; set; } - public DateTime CreateDate { get; set; } - public DateTime UpdateDate { get; set; } - public Guid Version { get; set; } - public int Level { get; set; } - - public PublishedItemType ItemType => PublishedItemType.Content; - public bool IsDraft(string culture = null) => false; - public bool IsPublished(string culture = null) => true; - - #endregion - - #region Tree - - public int ParentId { get; set; } - public IEnumerable ChildIds { get; set; } - - public IPublishedContent Parent { get; set; } - public IEnumerable Children { get; set; } - public IEnumerable ChildrenForAllCultures => Children; - - #endregion - - #region ContentType - - public IPublishedContentType ContentType { get; set; } - - #endregion - - #region Properties - - public IEnumerable Properties { get; set; } - - public IPublishedProperty GetProperty(string alias) - { - return Properties.FirstOrDefault(p => p.Alias.InvariantEquals(alias)); - } - - public IPublishedProperty GetProperty(string alias, bool recurse) - { - var property = GetProperty(alias); - if (recurse == false) return property; - - IPublishedContent content = this; - while (content != null && (property == null || property.HasValue() == false)) - { - content = content.Parent; - property = content?.GetProperty(alias); - } - - return property; - } - - public object this[string alias] - { - get - { - var property = GetProperty(alias); - return property == null || property.HasValue() == false ? null : property.GetValue(); - } - } - - #endregion - } - - public class SolidPublishedProperty : IPublishedProperty - { - public IPublishedPropertyType PropertyType { get; set; } - public string Alias { get; set; } - public object SolidSourceValue { get; set; } - public object SolidValue { get; set; } - public bool SolidHasValue { get; set; } - public object SolidXPathValue { get; set; } - - public virtual object GetSourceValue(string culture = null, string segment = null) => SolidSourceValue; - public virtual object GetValue(string culture = null, string segment = null) => SolidValue; - public virtual object GetXPathValue(string culture = null, string segment = null) => SolidXPathValue; - public virtual bool HasValue(string culture = null, string segment = null) => SolidHasValue; - } - - public class SolidPublishedPropertyWithLanguageVariants : SolidPublishedProperty - { - private readonly IDictionary _solidSourceValues = new Dictionary(); - private readonly IDictionary _solidValues = new Dictionary(); - private readonly IDictionary _solidXPathValues = new Dictionary(); - - public override object GetSourceValue(string culture = null, string segment = null) - { - if (string.IsNullOrEmpty(culture)) - { - return base.GetSourceValue(culture, segment); - } - - return _solidSourceValues.ContainsKey(culture) ? _solidSourceValues[culture] : null; - } - - public override object GetValue(string culture = null, string segment = null) - { - if (string.IsNullOrEmpty(culture)) - { - return base.GetValue(culture, segment); - } - - return _solidValues.ContainsKey(culture) ? _solidValues[culture] : null; - } - - public override object GetXPathValue(string culture = null, string segment = null) - { - if (string.IsNullOrEmpty(culture)) - { - return base.GetXPathValue(culture, segment); - } - - return _solidXPathValues.ContainsKey(culture) ? _solidXPathValues[culture] : null; - } - - public override bool HasValue(string culture = null, string segment = null) - { - if (string.IsNullOrEmpty(culture)) - { - return base.HasValue(culture, segment); - } - - return _solidSourceValues.ContainsKey(culture); - } - - public void SetSourceValue(string culture, object value, bool defaultValue = false) - { - _solidSourceValues.Add(culture, value); - if (defaultValue) - { - SolidSourceValue = value; - SolidHasValue = true; - } - } - - public void SetValue(string culture, object value, bool defaultValue = false) - { - _solidValues.Add(culture, value); - if (defaultValue) - { - SolidValue = value; - SolidHasValue = true; - } - } - - public void SetXPathValue(string culture, object value, bool defaultValue = false) - { - _solidXPathValues.Add(culture, value); - if (defaultValue) - { - SolidXPathValue = value; - } - } - } - - [PublishedModel("ContentType2")] - public class ContentType2 : PublishedContentModel - { - #region Plumbing - - public ContentType2(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - { } - - #endregion - - public int Prop1 => this.Value(Mock.Of(), "prop1"); - } - - [PublishedModel("ContentType2Sub")] - public class ContentType2Sub : ContentType2 - { - #region Plumbing - - public ContentType2Sub(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - { } - - #endregion - } - - public class PublishedContentStrong1 : PublishedContentModel - { - public PublishedContentStrong1(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - { } - - public int StrongValue => this.Value(Mock.Of(), "strongValue"); - } - - public class PublishedContentStrong1Sub : PublishedContentStrong1 - { - public PublishedContentStrong1Sub(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - { } - - public int AnotherValue => this.Value(Mock.Of(), "anotherValue"); - } - - public class PublishedContentStrong2 : PublishedContentModel - { - public PublishedContentStrong2(IPublishedContent content, IPublishedValueFallback fallback) - : base(content, fallback) - { } - - public int StrongValue => this.Value(Mock.Of(), "strongValue"); - } - - public class AutoPublishedContentType : PublishedContentType - { - private static readonly IPublishedPropertyType Default; - - static AutoPublishedContentType() - { - var serializer = new ConfigurationEditorJsonSerializer(); - var dataTypeServiceMock = new Mock(); - var dataType = new DataType(new VoidEditor(Mock.Of()), serializer) - { Id = 666 }; - dataTypeServiceMock.Setup(x => x.GetAll()).Returns(dataType.Yield); - - var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeServiceMock.Object); - Default = factory.CreatePropertyType("*", 666); - } - - public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable propertyTypes) - : base(key, id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) - { } - - public AutoPublishedContentType(Guid key, int id, string alias, Func> propertyTypes) - : base(key, id, alias, PublishedItemType.Content, Enumerable.Empty(), propertyTypes, ContentVariation.Nothing) - { } - - public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable compositionAliases, IEnumerable propertyTypes) - : base(key, id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) - { } - - public AutoPublishedContentType(Guid key, int id, string alias, IEnumerable compositionAliases, Func> propertyTypes) - : base(key, id, alias, PublishedItemType.Content, compositionAliases, propertyTypes, ContentVariation.Nothing) - { } - - public override IPublishedPropertyType GetPropertyType(string alias) - { - var propertyType = base.GetPropertyType(alias); - return propertyType ?? Default; - } - } -} diff --git a/tests/Umbraco.Tests/PublishedContent/StronglyTypedModels/Home.cs b/tests/Umbraco.Tests/PublishedContent/StronglyTypedModels/Home.cs deleted file mode 100644 index d9a3807c2599..000000000000 --- a/tests/Umbraco.Tests/PublishedContent/StronglyTypedModels/Home.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Web; -using Umbraco.Core.Models; - -namespace Umbraco.Tests.PublishedContent.StronglyTypedModels -{ - /// - /// Used for testing strongly-typed published content extensions that work against the - /// - public class Home : TypedModelBase - { - public Home(IPublishedContent publishedContent) : base(publishedContent) - { - } - } -} diff --git a/tests/Umbraco.Tests/Routing/ContentFinderByIdTests.cs b/tests/Umbraco.Tests/Routing/ContentFinderByIdTests.cs deleted file mode 100644 index 652706377caa..000000000000 --- a/tests/Umbraco.Tests/Routing/ContentFinderByIdTests.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using NUnit.Framework; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Web; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - public class ContentFinderByIdTests : BaseWebTest - { - [TestCase("/1046", 1046)] - [TestCase("/1046.aspx", 1046)] - public async Task Lookup_By_Id(string urlAsString, int nodeMatch) - { - var umbracoContext = GetUmbracoContext(urlAsString); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - var webRoutingSettings = new WebRoutingSettings(); - var lookup = new ContentFinderByIdPath(Microsoft.Extensions.Options.Options.Create(webRoutingSettings), LoggerFactory.CreateLogger(), Factory.GetRequiredService(), GetUmbracoContextAccessor(umbracoContext)); - - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(frequest.PublishedContent.Id, nodeMatch); - } - } -} diff --git a/tests/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs b/tests/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs deleted file mode 100644 index 6372dc74248e..000000000000 --- a/tests/Umbraco.Tests/Routing/ContentFinderByPageIdQueryTests.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Threading.Tasks; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Web; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - public class ContentFinderByPageIdQueryTests : BaseWebTest - { - [TestCase("/?umbPageId=1046", 1046)] - [TestCase("/?UMBPAGEID=1046", 1046)] - [TestCase("/default.aspx?umbPageId=1046", 1046)] // TODO: Should this match?? - [TestCase("/some/other/page?umbPageId=1046", 1046)] // TODO: Should this match?? - [TestCase("/some/other/page.aspx?umbPageId=1046", 1046)] // TODO: Should this match?? - public async Task Lookup_By_Page_Id(string urlAsString, int nodeMatch) - { - var umbracoContext = GetUmbracoContext(urlAsString); - var httpContext = GetHttpContextFactory(urlAsString).HttpContext; - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - var mockRequestAccessor = new Mock(); - mockRequestAccessor.Setup(x => x.GetRequestValue("umbPageID")).Returns(httpContext.Request.QueryString["umbPageID"]); - - var lookup = new ContentFinderByPageIdQuery(mockRequestAccessor.Object, GetUmbracoContextAccessor(umbracoContext)); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(frequest.PublishedContent.Id, nodeMatch); - } - } -} diff --git a/tests/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs b/tests/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs deleted file mode 100644 index a7d256c9ed65..000000000000 --- a/tests/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using NUnit.Framework; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class ContentFinderByUrlAndTemplateTests : BaseWebTest - { - Template CreateTemplate(string alias) - { - var template = new Template(ShortStringHelper, alias, alias); - template.Content = ""; // else saving throws with a dirty internal error - ServiceContext.FileService.SaveTemplate(template); - return template; - } - - [TestCase("/blah")] - [TestCase("/default.aspx/blah")] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! - [TestCase("/home/Sub1/blah")] - [TestCase("/Home/Sub1/Blah")] //different cases - [TestCase("/home/Sub1.aspx/blah")] - public async Task Match_Document_By_Url_With_Template(string urlAsString) - { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; - - var template1 = CreateTemplate("test"); - var template2 = CreateTemplate("blah"); - var umbracoContext = GetUmbracoContext(urlAsString, template1.Id, globalSettings: globalSettings); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var reqBuilder = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - var webRoutingSettings = new WebRoutingSettings(); - var lookup = new ContentFinderByUrlAndTemplate( - LoggerFactory.CreateLogger(), - ServiceContext.FileService, - ServiceContext.ContentTypeService, - GetUmbracoContextAccessor(umbracoContext), - Microsoft.Extensions.Options.Options.Create(webRoutingSettings)); - - var result = lookup.TryFindContent(reqBuilder); - - IPublishedRequest frequest = reqBuilder.Build(); - - Assert.IsTrue(result); - Assert.IsNotNull(frequest.PublishedContent); - var templateAlias = frequest.GetTemplateAlias(); - Assert.IsNotNull(templateAlias ); - Assert.AreEqual("blah".ToUpperInvariant(), templateAlias.ToUpperInvariant()); - } - } -} diff --git a/tests/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs b/tests/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs deleted file mode 100644 index 35f06627fede..000000000000 --- a/tests/Umbraco.Tests/Routing/ContentFinderByUrlTests.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using NUnit.Framework; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class ContentFinderByUrlTests : BaseWebTest - { - [TestCase("/", 1046)] - [TestCase("/default.aspx", 1046)] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! - [TestCase("/Sub1", 1173)] - [TestCase("/sub1", 1173)] - [TestCase("/sub1.aspx", 1173)] - [TestCase("/home/sub1", -1)] // should fail - - // these two are special. getNiceUrl(1046) returns "/" but getNiceUrl(1172) cannot also return "/" so - // we've made it return "/test-page" => we have to support that URL back in the lookup... - [TestCase("/home", 1046)] - [TestCase("/test-page", 1172)] - public async Task Match_Document_By_Url_Hide_Top_Level(string urlString, int expectedId) - { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = true }; - - var snapshotService = CreatePublishedSnapshotService(globalSettings); - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettings, snapshotService: snapshotService); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var frequest = await publishedRouter.CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); - - Assert.IsTrue(globalSettings.HideTopLevelNodeFromPath); - - // FIXME: debugging - going further down, the routes cache is NOT empty?! - if (urlString == "/home/sub1") - System.Diagnostics.Debugger.Break(); - - var result = lookup.TryFindContent(frequest); - - if (expectedId > 0) - { - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - else - { - Assert.IsFalse(result); - } - } - - [TestCase("/", 1046)] - [TestCase("/default.aspx", 1046)] //this one is actually rather important since this is the path that comes through when we are running in pre-IIS 7 for the root document '/' ! - [TestCase("/home", 1046)] - [TestCase("/home/Sub1", 1173)] - [TestCase("/Home/Sub1", 1173)] //different cases - [TestCase("/home/Sub1.aspx", 1173)] - public async Task Match_Document_By_Url(string urlString, int expectedId) - { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; - - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettings); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); - - Assert.IsFalse(globalSettings.HideTopLevelNodeFromPath); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - /// - /// This test handles requests with special characters in the URL. - /// - /// - /// - [TestCase("/", 1046)] - [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] - [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] - public async Task Match_Document_By_Url_With_Special_Characters(string urlString, int expectedId) - { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; - - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettings); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - - /// - /// This test handles requests with a hostname associated. - /// The logic for handling this goes through the DomainHelper and is a bit different - /// from what happens in a normal request - so it has a separate test with a mocked - /// hostname added. - /// - /// - /// - [TestCase("/", 1046)] - [TestCase("/home/sub1/custom-sub-3-with-accént-character", 1179)] - [TestCase("/home/sub1/custom-sub-4-with-æøå", 1180)] - public async Task Match_Document_By_Url_With_Special_Characters_Using_Hostname(string urlString, int expectedId) - { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; - - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettings); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite", -1, "en-US", false), new Uri("http://mysite/"))); - var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - - /// - /// This test handles requests with a hostname with special characters associated. - /// The logic for handling this goes through the DomainHelper and is a bit different - /// from what happens in a normal request - so it has a separate test with a mocked - /// hostname added. - /// - /// - /// - [TestCase("/æøå/", 1046)] - [TestCase("/æøå/home/sub1", 1173)] - [TestCase("/æøå/home/sub1/custom-sub-3-with-accént-character", 1179)] - [TestCase("/æøå/home/sub1/custom-sub-4-with-æøå", 1180)] - public async Task Match_Document_By_Url_With_Special_Characters_In_Hostname(string urlString, int expectedId) - { - var globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = false }; - - var umbracoContext = GetUmbracoContext(urlString, globalSettings:globalSettings); - var publishedRouter = CreatePublishedRouter(GetUmbracoContextAccessor(umbracoContext)); - var frequest = await publishedRouter .CreateRequestAsync(umbracoContext.CleanedUmbracoUrl); - frequest.SetDomain(new DomainAndUri(new Domain(1, "mysite/æøå", -1, "en-US", false), new Uri("http://mysite/æøå"))); - var lookup = new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbracoContext)); - - var result = lookup.TryFindContent(frequest); - - Assert.IsTrue(result); - Assert.AreEqual(expectedId, frequest.PublishedContent.Id); - } - } -} diff --git a/tests/Umbraco.Tests/Routing/GetContentUrlsTests.cs b/tests/Umbraco.Tests/Routing/GetContentUrlsTests.cs deleted file mode 100644 index fe371e730237..000000000000 --- a/tests/Umbraco.Tests/Routing/GetContentUrlsTests.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Services; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers.Entities; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - public class GetContentUrlsTests : UrlRoutingTestBase - { - private GlobalSettings _globalSettings; - private WebRoutingSettings _webRoutingSettings; - private RequestHandlerSettings _requestHandlerSettings; - - public override void SetUp() - { - base.SetUp(); - - _globalSettings = new GlobalSettings(); - _webRoutingSettings = new WebRoutingSettings(); - _requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - } - - private ILocalizedTextService GetTextService() - { - var textService = Mock.Of( - x => x.Localize("content/itemNotPublished", - It.IsAny(), - It.IsAny>()) == "content/itemNotPublished"); - return textService; - } - - private ILocalizationService GetLangService(params string[] isoCodes) - { - var allLangs = isoCodes - .Select(CultureInfo.GetCultureInfo) - .Select(culture => new Language(_globalSettings, culture.Name) - { - CultureName = culture.DisplayName, - IsDefault = true, - IsMandatory = true - }).ToArray(); - - var langService = Mock.Of(x => x.GetAllLanguages() == allLangs); - return langService; - } - - [Test] - public async Task Content_Not_Published() - { - var contentType = MockedContentTypes.CreateBasicContentType(); - var content = MockedContent.CreateBasicContent(contentType); - content.Id = 1046; // FIXME: we are using this ID only because it's built into the test XML published cache - content.Path = "-1,1046"; - - var umbContext = GetUmbracoContext("http://localhost:8000"); - var publishedRouter = CreatePublishedRouter( - GetUmbracoContextAccessor(umbContext), - Factory, - contentFinders: new ContentFinderCollection(new[] { new ContentFinderByUrl(LoggerFactory.CreateLogger(), GetUmbracoContextAccessor(umbContext)) })); - var urls = (await content.GetContentUrlsAsync(publishedRouter, - umbContext, - GetLangService("en-US", "fr-FR"), GetTextService(), ServiceContext.ContentService, - VariationContextAccessor, - LoggerFactory.CreateLogger(), - UriUtility, - PublishedUrlProvider)).ToList(); - - Assert.AreEqual(1, urls.Count); - Assert.AreEqual("content/itemNotPublished", urls[0].Text); - Assert.IsFalse(urls[0].IsUrl); - } - - [Test] - public async Task Invariant_Root_Content_Published_No_Domains() - { - var contentType = MockedContentTypes.CreateBasicContentType(); - var content = MockedContent.CreateBasicContent(contentType); - content.Id = 1046; // FIXME: we are using this ID only because it's built into the test XML published cache - content.Path = "-1,1046"; - content.Published = true; - - var umbContext = GetUmbracoContext("http://localhost:8000"); - var umbracoContextAccessor = GetUmbracoContextAccessor(umbContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(_requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), - umbracoContextAccessor, UriUtility); - var publishedUrlProvider = new UrlProvider( - umbracoContextAccessor, - Microsoft.Extensions.Options.Options.Create(_webRoutingSettings), - new UrlProviderCollection(new []{urlProvider}), - new MediaUrlProviderCollection(Enumerable.Empty()), - Mock.Of() - ); - - var publishedRouter = CreatePublishedRouter( - umbracoContextAccessor, - Factory, - contentFinders:new ContentFinderCollection(new[]{new ContentFinderByUrl(LoggerFactory.CreateLogger(), umbracoContextAccessor) })); - var urls = (await content.GetContentUrlsAsync(publishedRouter, - umbContext, - GetLangService("en-US", "fr-FR"), GetTextService(), ServiceContext.ContentService, - VariationContextAccessor, - LoggerFactory.CreateLogger(), - UriUtility, - publishedUrlProvider)).ToList(); - - Assert.AreEqual(1, urls.Count); - Assert.AreEqual("/home/", urls[0].Text); - Assert.AreEqual("en-US", urls[0].Culture); - Assert.IsTrue(urls[0].IsUrl); - } - - [Test] - public async Task Invariant_Child_Content_Published_No_Domains() - { - var contentType = MockedContentTypes.CreateBasicContentType(); - var parent = MockedContent.CreateBasicContent(contentType); - parent.Id = 1046; // FIXME: we are using this ID only because it's built into the test XML published cache - parent.Name = "home"; - parent.Path = "-1,1046"; - parent.Published = true; - var child = MockedContent.CreateBasicContent(contentType); - child.Name = "sub1"; - child.Id = 1173; // FIXME: we are using this ID only because it's built into the test XML published cache - child.Path = "-1,1046,1173"; - child.Published = true; - - var umbContext = GetUmbracoContext("http://localhost:8000"); - var umbracoContextAccessor = GetUmbracoContextAccessor(umbContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(_requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = new UrlProvider( - umbracoContextAccessor, - Microsoft.Extensions.Options.Options.Create(_webRoutingSettings), - new UrlProviderCollection(new []{urlProvider}), - new MediaUrlProviderCollection(Enumerable.Empty()), - Mock.Of() - ); - - var publishedRouter = CreatePublishedRouter( - umbracoContextAccessor, - Factory, - contentFinders: new ContentFinderCollection(new[] { new ContentFinderByUrl(LoggerFactory.CreateLogger(), umbracoContextAccessor) })); - var urls = (await child.GetContentUrlsAsync(publishedRouter, - umbContext, - GetLangService("en-US", "fr-FR"), GetTextService(), ServiceContext.ContentService, - VariationContextAccessor, - LoggerFactory.CreateLogger(), - UriUtility, - publishedUrlProvider - )).ToList(); - - Assert.AreEqual(1, urls.Count); - Assert.AreEqual("/home/sub1/", urls[0].Text); - Assert.AreEqual("en-US", urls[0].Culture); - Assert.IsTrue(urls[0].IsUrl); - } - - // TODO: We need a lot of tests here, the above was just to get started with being able to unit test this method - // * variant URLs without domains assigned, what happens? - // * variant URLs with domains assigned, but also having more languages installed than there are domains/cultures assigned - // * variant URLs with an ancestor culture unpublished - // * invariant URLs with ancestors as variants - // * ... probably a lot more - - } -} diff --git a/tests/Umbraco.Tests/Routing/RouteTestExtensions.cs b/tests/Umbraco.Tests/Routing/RouteTestExtensions.cs deleted file mode 100644 index 642488c25627..000000000000 --- a/tests/Umbraco.Tests/Routing/RouteTestExtensions.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Web; -using System.Web.Routing; -using Moq; -using Umbraco.Tests.TestHelpers; - -namespace Umbraco.Tests.Routing -{ - public static class RouteTestExtensions - { - - /// - /// Return the route data for the URL based on a mocked context - /// - /// - /// - /// - public static RouteData GetDataForRoute(this RouteCollection routes, string requestUrl) - { - var context = new FakeHttpContextFactory(requestUrl); - return routes.GetDataForRoute(context.HttpContext); - } - - /// - /// Get the route data based on the URL and HTTP context - /// - /// - /// - /// - public static RouteData GetDataForRoute(this RouteCollection routes, HttpContextBase httpContext) - { - var data = routes.GetRouteData(httpContext); - - //set the route data on the request context - var requestMock = Mock.Get(httpContext.Request.RequestContext); - requestMock.Setup(x => x.RouteData).Returns(data); - - return data; - } - - /// - /// Checks if the URL will be ignored in the RouteTable - /// - /// - /// - /// - /// MVCContrib has a similar one but is faulty: - /// http://mvccontrib.codeplex.com/workitem/7173 - /// - public static bool ShouldIgnoreRoute(this string url) - { - var http = new FakeHttpContextFactory(url); - var r = RouteTable.Routes.GetRouteData(http.HttpContext); - if (r == null) return false; - return (r.RouteHandler is StopRoutingHandler); - } - - } -} diff --git a/tests/Umbraco.Tests/Routing/RoutesCacheTests.cs b/tests/Umbraco.Tests/Routing/RoutesCacheTests.cs deleted file mode 100644 index ba851bf76127..000000000000 --- a/tests/Umbraco.Tests/Routing/RoutesCacheTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using NUnit.Framework; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Tests.LegacyXmlPublishedCache; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class RoutesCacheTests : BaseWebTest - { - [Test] - public void U4_7939() - { - //var routingContext = GetRoutingContext("/test", 1111); - var umbracoContext = GetUmbracoContext("/test", 0); - var cache = umbracoContext.PublishedSnapshot.Content as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - - // FIXME: not sure? - //PublishedContentCache.UnitTesting = false; // else does not write to routes cache - //Assert.IsFalse(PublishedContentCache.UnitTesting); - - var z = cache.GetByRoute(false, "/home/sub1"); - Assert.IsNotNull(z); - Assert.AreEqual(1173, z.Id); - - var routes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual(1, routes.Count); - - // before the fix, the following assert would fail because the route would - // have been stored as { 0, "/home/sub1" } - essentially meaning we were NOT - // storing anything in the route cache! - - Assert.AreEqual(1173, routes.Keys.First()); - Assert.AreEqual("/home/sub1", routes.Values.First()); - } - } -} diff --git a/tests/Umbraco.Tests/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs b/tests/Umbraco.Tests/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs deleted file mode 100644 index 3a2ccc23dfdf..000000000000 --- a/tests/Umbraco.Tests/Routing/UrlProviderWithHideTopLevelNodeFromPathTests.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.Extensions.Logging; -using NUnit.Framework; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Tests.Common; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.Testing; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class UrlProviderWithHideTopLevelNodeFromPathTests : BaseUrlProviderTest - { - private GlobalSettings _globalSettings; - - public override void SetUp() - { - _globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = HideTopLevelNodeFromPath }; - base.SetUp(); - PublishedSnapshotService = CreatePublishedSnapshotService(_globalSettings); - - - } - - protected override bool HideTopLevelNodeFromPath => true; - - protected override void ComposeSettings() - { - base.ComposeSettings(); - Builder.Services.AddUnique(x => Microsoft.Extensions.Options.Options.Create(_globalSettings)); - } - - [TestCase(1046, "/")] - [TestCase(1173, "/sub1/")] - [TestCase(1174, "/sub1/sub2/")] - [TestCase(1176, "/sub1/sub-3/")] - [TestCase(1177, "/sub1/custom-sub-1/")] - [TestCase(1178, "/sub1/custom-sub-2/")] - [TestCase(1175, "/sub-2/")] - [TestCase(1172, "/test-page/")] // not hidden because not first root - public void Get_Url_Hiding_Top_Level(int nodeId, string niceUrlMatch) - { - var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - - var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: _globalSettings, snapshotService:PublishedSnapshotService); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - - var result = publishedUrlProvider.GetUrl(nodeId); - Assert.AreEqual(niceUrlMatch, result); - } - } -} diff --git a/tests/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs b/tests/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs deleted file mode 100644 index bbadf897d073..000000000000 --- a/tests/Umbraco.Tests/Routing/UrlProviderWithoutHideTopLevelNodeFromPathTests.cs +++ /dev/null @@ -1,311 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Tests.Common; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Tests.LegacyXmlPublishedCache; -using Umbraco.Tests.PublishedContent; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public class UrlProviderWithoutHideTopLevelNodeFromPathTests : BaseUrlProviderTest - { - private readonly GlobalSettings _globalSettings; - - public UrlProviderWithoutHideTopLevelNodeFromPathTests() - { - _globalSettings = new GlobalSettings { HideTopLevelNodeFromPath = HideTopLevelNodeFromPath }; - } - - protected override bool HideTopLevelNodeFromPath => false; - - protected override void ComposeSettings() - { - base.ComposeSettings(); - Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(_globalSettings)); - } - - /// - /// This checks that when we retrieve a NiceUrl for multiple items that there are no issues with cache overlap - /// and that they are all cached correctly. - /// - [Test] - public void Ensure_Cache_Is_Correct() - { - var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = false }; - - var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: _globalSettings); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - - var samples = new Dictionary { - { 1046, "/home" }, - { 1173, "/home/sub1" }, - { 1174, "/home/sub1/sub2" }, - { 1176, "/home/sub1/sub-3" }, - { 1177, "/home/sub1/custom-sub-1" }, - { 1178, "/home/sub1/custom-sub-2" }, - { 1175, "/home/sub-2" }, - { 1172, "/test-page" } - }; - - foreach (var sample in samples) - { - var result = publishedUrlProvider.GetUrl(sample.Key); - Assert.AreEqual(sample.Value, result); - } - - var randomSample = new KeyValuePair(1177, "/home/sub1/custom-sub-1"); - for (int i = 0; i < 5; i++) - { - var result = publishedUrlProvider.GetUrl(randomSample.Key); - Assert.AreEqual(randomSample.Value, result); - } - - var cache = umbracoContext.Content as PublishedContentCache; - if (cache == null) throw new Exception("Unsupported IPublishedContentCache, only the Xml one is supported."); - var cachedRoutes = cache.RoutesCache.GetCachedRoutes(); - Assert.AreEqual(8, cachedRoutes.Count); - - foreach (var sample in samples) - { - Assert.IsTrue(cachedRoutes.ContainsKey(sample.Key)); - Assert.AreEqual(sample.Value, cachedRoutes[sample.Key]); - } - - var cachedIds = cache.RoutesCache.GetCachedIds(); - Assert.AreEqual(0, cachedIds.Count); - } - - [TestCase(1046, "/home/")] - [TestCase(1173, "/home/sub1/")] - [TestCase(1174, "/home/sub1/sub2/")] - [TestCase(1176, "/home/sub1/sub-3/")] - [TestCase(1177, "/home/sub1/custom-sub-1/")] - [TestCase(1178, "/home/sub1/custom-sub-2/")] - [TestCase(1175, "/home/sub-2/")] - [TestCase(1172, "/test-page/")] - public void Get_Url_Not_Hiding_Top_Level(int nodeId, string niceUrlMatch) - { - var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - - var umbracoContext = GetUmbracoContext("/test", 1111, globalSettings: _globalSettings); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - - var result = publishedUrlProvider.GetUrl(nodeId); - Assert.AreEqual(niceUrlMatch, result); - } - - [Test] - public void Get_Url_For_Culture_Variant_Without_Domains_Non_Current_Url() - { - const string currentUri = "http://example.us/test"; - - var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - - var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); - var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; - - var publishedContentCache = new Mock(); - publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) - .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard - publishedContentCache.Setup(x => x.GetById(It.IsAny())) - .Returns(id => id == 1234 ? publishedContent : null); - - var domainCache = new Mock(); - domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) - .Returns((int contentId, bool includeWildcards) => Enumerable.Empty()); - - var snapshot = Mock.Of(x => x.Content == publishedContentCache.Object && x.Domains == domainCache.Object); - - var snapshotService = new Mock(); - snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) - .Returns(snapshot); - - var umbracoContext = GetUmbracoContext(currentUri, - globalSettings: _globalSettings, - snapshotService: snapshotService.Object); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - - //even though we are asking for a specific culture URL, there are no domains assigned so all that can be returned is a normal relative URL. - var url = publishedUrlProvider.GetUrl(1234, culture: "fr-FR"); - - Assert.AreEqual("/home/test-fr/", url); - } - - /// - /// This tests DefaultUrlProvider.GetUrl with a specific culture when the current URL is the culture specific domain - /// - [Test] - public void Get_Url_For_Culture_Variant_With_Current_Url() - { - const string currentUri = "http://example.fr/test"; - - var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - - var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); - var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; - - var publishedContentCache = new Mock(); - publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) - .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard - publishedContentCache.Setup(x => x.GetById(It.IsAny())) - .Returns(id => id == 1234 ? publishedContent : null); - - var domainCache = new Mock(); - domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) - .Returns((int contentId, bool includeWildcards) => - { - if (contentId != 9876) return Enumerable.Empty(); - return new[] - { - new Domain(2, "example.us", 9876, "en-US", false), //default - new Domain(3, "example.fr", 9876, "fr-FR", false) - }; - }); - domainCache.Setup(x => x.DefaultCulture).Returns(CultureInfo.GetCultureInfo("en-US").Name); - - var snapshot = Mock.Of(x => x.Content == publishedContentCache.Object && x.Domains == domainCache.Object); - - var snapshotService = new Mock(); - snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) - .Returns(snapshot); - - var umbracoContext = GetUmbracoContext(currentUri, - globalSettings: _globalSettings, - snapshotService: snapshotService.Object); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - - var url = publishedUrlProvider.GetUrl(1234, culture: "fr-FR"); - - Assert.AreEqual("/home/test-fr/", url); - } - - /// - /// This tests DefaultUrlProvider.GetUrl with a specific culture when the current URL is not the culture specific domain - /// - [Test] - public void Get_Url_For_Culture_Variant_Non_Current_Url() - { - const string currentUri = "http://example.us/test"; - - var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - - var contentType = new PublishedContentType(Guid.NewGuid(), 666, "alias", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Culture); - var publishedContent = new SolidPublishedContent(contentType) { Id = 1234 }; - - var publishedContentCache = new Mock(); - publishedContentCache.Setup(x => x.GetRouteById(1234, "fr-FR")) - .Returns("9876/home/test-fr"); //prefix with the root id node with the domain assigned as per the umbraco standard - publishedContentCache.Setup(x => x.GetById(It.IsAny())) - .Returns(id => id == 1234 ? publishedContent : null); - - var domainCache = new Mock(); - domainCache.Setup(x => x.GetAssigned(It.IsAny(), false)) - .Returns((int contentId, bool includeWildcards) => - { - if (contentId != 9876) return Enumerable.Empty(); - return new[] - { - new Domain(2, "example.us", 9876, "en-US", false), //default - new Domain(3, "example.fr", 9876, "fr-FR", false) - }; - }); - domainCache.Setup(x => x.DefaultCulture).Returns(CultureInfo.GetCultureInfo("en-US").Name); - - var snapshot = Mock.Of(x => x.Content == publishedContentCache.Object && x.Domains == domainCache.Object); - - var snapshotService = new Mock(); - snapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())) - .Returns(snapshot); - - var umbracoContext = GetUmbracoContext(currentUri, - globalSettings: _globalSettings, - snapshotService: snapshotService.Object); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - - - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - var url = publishedUrlProvider.GetUrl(1234, culture: "fr-FR"); - - //the current uri is not the culture specific domain we want, so the result is an absolute path to the culture specific domain - Assert.AreEqual("http://example.fr/home/test-fr/", url); - } - - [Test] - public void Get_Url_Relative_Or_Absolute() - { - var requestHandlerSettings = new RequestHandlerSettings { AddTrailingSlash = true }; - - var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, globalSettings: _globalSettings); - var umbracoContextAccessor = new TestUmbracoContextAccessor(umbracoContext); - var urlProvider = new DefaultUrlProvider( - Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), umbracoContextAccessor, UriUtility); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - - Assert.AreEqual("/home/sub1/custom-sub-1/", publishedUrlProvider.GetUrl(1177)); - - publishedUrlProvider.Mode = UrlMode.Absolute; - Assert.AreEqual("http://example.com/home/sub1/custom-sub-1/", publishedUrlProvider.GetUrl(1177)); - } - - [Test] - public void Get_Url_Unpublished() - { - var requestHandlerSettings = new RequestHandlerSettings(); - - var urlProvider = new DefaultUrlProvider(Microsoft.Extensions.Options.Options.Create(requestHandlerSettings), - LoggerFactory.CreateLogger(), - new SiteDomainMapper(), UmbracoContextAccessor, UriUtility); - var umbracoContext = GetUmbracoContext("http://example.com/test", 1111, globalSettings: _globalSettings); - var publishedUrlProvider = GetPublishedUrlProvider(umbracoContext, urlProvider); - - //mock the Umbraco settings that we need - - Assert.AreEqual("#", publishedUrlProvider.GetUrl(999999)); - - publishedUrlProvider.Mode = UrlMode.Absolute; - - Assert.AreEqual("#", publishedUrlProvider.GetUrl(999999)); - } - } -} diff --git a/tests/Umbraco.Tests/Routing/UrlRoutingTestBase.cs b/tests/Umbraco.Tests/Routing/UrlRoutingTestBase.cs deleted file mode 100644 index e2f195b09d46..000000000000 --- a/tests/Umbraco.Tests/Routing/UrlRoutingTestBase.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.Testing; - -namespace Umbraco.Tests.Routing -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)] - public abstract class UrlRoutingTestBase : BaseWebTest - { - /// - /// Sets up the mock domain service - /// - /// - protected IDomainService SetupDomainServiceMock(IEnumerable allDomains) - { - var domainService = Mock.Get(ServiceContext.DomainService); - //setup mock domain service - domainService.Setup(service => service.GetAll(It.IsAny())) - .Returns((bool incWildcards) => incWildcards ? allDomains : allDomains.Where(d => d.IsWildcard == false)); - domainService.Setup(service => service.GetAssignedDomains(It.IsAny(), It.IsAny())) - .Returns((int id, bool incWildcards) => allDomains.Where(d => d.RootContentId == id && (incWildcards || d.IsWildcard == false))); - return domainService.Object; - } - - protected override void Compose() - { - base.Compose(); - - Builder.Services.AddUnique(GetServiceContext()); - } - - protected ServiceContext GetServiceContext() - { - // get the mocked service context to get the mocked domain service - var serviceContext = TestObjects.GetServiceContextMock(Factory); - - //setup mock domain service - var domainService = Mock.Get(serviceContext.DomainService); - domainService.Setup(service => service.GetAll(It.IsAny())) - .Returns((bool incWildcards) => new[] - { - new UmbracoDomain("domain1.com/"){Id = 1, LanguageId = LangDeId, RootContentId = 1001, LanguageIsoCode = "de-DE"}, - new UmbracoDomain("domain1.com/en"){Id = 1, LanguageId = LangEngId, RootContentId = 10011, LanguageIsoCode = "en-US"}, - new UmbracoDomain("domain1.com/fr"){Id = 1, LanguageId = LangFrId, RootContentId = 10012, LanguageIsoCode = "fr-FR"} - }); - - return serviceContext; - } - - public const int LangDeId = 333; - public const int LangEngId = 334; - public const int LangFrId = 335; - public const int LangCzId = 336; - public const int LangNlId = 337; - public const int LangDkId = 338; - } -} diff --git a/tests/Umbraco.Tests/Scoping/PassThroughEventDispatcherTests.cs b/tests/Umbraco.Tests/Scoping/PassThroughEventDispatcherTests.cs deleted file mode 100644 index ca4767c0f114..000000000000 --- a/tests/Umbraco.Tests/Scoping/PassThroughEventDispatcherTests.cs +++ /dev/null @@ -1,59 +0,0 @@ -using NUnit.Framework; - -namespace Umbraco.Tests.Scoping -{ - [TestFixture] - [NUnit.Framework.Ignore("Cannot dispatch events on NoScope!")] - public class PassThroughEventDispatcherTests - { - // FIXME: so... should we remove, or enable, these tests? - - // [Test] - // public void TriggersCancelableEvents() - // { - // var counter = 0; - - // DoThing1 += (sender, args) => { counter++; }; - - // var scopeProvider = new ScopeProvider(Mock.Of()); - // var events = scopeProvider.GetAmbientOrNoScope().Events; - // events.DispatchCancelable(DoThing1, this, new CancellableEventArgs()); - - // Assert.AreEqual(1, counter); - // } - - // [Test] - // public void TriggersEvents() - // { - // var counter = 0; - - // DoThing1 += (sender, args) => { counter++; }; - // DoThing2 += (sender, args) => { counter++; }; - // DoThing3 += (sender, args) => { counter++; }; - - // var scopeProvider = new ScopeProvider(Mock.Of()); - // var events = scopeProvider.GetAmbientOrNoScope().Events; - // events.Dispatch(DoThing1, this, new EventArgs()); - // events.Dispatch(DoThing2, this, new EventArgs()); - // events.Dispatch(DoThing3, this, new EventArgs()); - - // Assert.AreEqual(3, counter); - // } - - // [Test] - // public void DoesNotQueueEvents() - // { - // var scopeProvider = new ScopeProvider(Mock.Of()); - // var events = scopeProvider.GetAmbientOrNoScope().Events; - // events.Dispatch(DoThing1, this, new EventArgs()); - // events.Dispatch(DoThing2, this, new EventArgs()); - // events.Dispatch(DoThing3, this, new EventArgs()); - - // Assert.IsEmpty(events.GetEvents(EventDefinitionFilter.All)); - // } - - // public event EventHandler DoThing1; - // public event EventHandler DoThing2; - // public event TypedEventHandler DoThing3; - } -} diff --git a/tests/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/tests/Umbraco.Tests/Scoping/ScopedXmlTests.cs deleted file mode 100644 index 53ef2e0246a7..000000000000 --- a/tests/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ /dev/null @@ -1,303 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml; -using Microsoft.Extensions.DependencyInjection; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Notifications; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Sync; -using Umbraco.Cms.Infrastructure.Sync; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.LegacyXmlPublishedCache; -using Umbraco.Tests.TestHelpers; -using Umbraco.Web.Composing; - -namespace Umbraco.Tests.Scoping -{ - [TestFixture] - [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true)] - public class ScopedXmlTests : TestWithDatabaseBase - { - private DistributedCacheBinder _distributedCacheBinder; - - protected override void Compose() - { - base.Compose(); - - // the cache refresher component needs to trigger to refresh caches - // but then, it requires a lot of plumbing ;( - // FIXME: and we cannot inject a DistributedCache yet - // so doing all this mess - Builder.Services.AddUnique(); - Builder.Services.AddUnique(f => Mock.Of()); - Builder.WithCollectionBuilder() - .Add(() => Builder.TypeLoader.GetCacheRefreshers()); - Builder.AddNotificationHandler(); - } - - protected override void ComposeSettings() - { - var contentSettings = new ContentSettings(); - var globalSettings = new GlobalSettings(); - var userPasswordConfigurationSettings = new UserPasswordConfigurationSettings(); - - Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(contentSettings)); - Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(globalSettings)); - Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(userPasswordConfigurationSettings)); - } - - - public class NotificationHandler : INotificationHandler - { - public void Handle(ContentPublishedNotification notification) => PublishedContent?.Invoke(notification); - - public static Action PublishedContent { get; set; } - } - - [TearDown] - public void Teardown() - { - NotificationHandler.PublishedContent = null; - SafeXmlReaderWriter.Cloning = null; - } - - // in 7.6, content.Instance - // .XmlContent - comes from .XmlContentInternal and is cached in context items for current request - // .XmlContentInternal - the actual main xml document - // become in 8 - // xmlStore.Xml - the actual main xml document - // publishedContentCache.GetXml() - the captured xml - - private static XmlStore XmlStore => (Current.Factory.GetRequiredService() as XmlPublishedSnapshotService).XmlStore; - private static XmlDocument XmlMaster => XmlStore.Xml; - private static XmlDocument XmlInContext => ((PublishedContentCache) Umbraco.Web.Composing.Current.UmbracoContext.Content).GetXml(false); - - [TestCase(true)] - [TestCase(false)] - public void TestScope(bool complete) - { - var umbracoContext = GetUmbracoContext("http://example.com/", setSingleton: true); - - // sanity checks - Assert.AreSame(umbracoContext, Umbraco.Web.Composing.Current.UmbracoContext); - Assert.AreSame(XmlStore, ((PublishedContentCache) umbracoContext.Content).XmlStore); - - // create document type, document - var contentType = new ContentType(ShortStringHelper, -1) { Alias = "CustomDocument", Name = "Custom Document" }; - ServiceContext.ContentTypeService.Save(contentType); - var item = new Content("name", -1, contentType); - - // wire cache refresher - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(Current.ServerMessenger, Current.CacheRefreshers)); - - // check xml in context = "before" - var xml = XmlInContext; - var beforeXml = xml; - var beforeOuterXml = beforeXml.OuterXml; - Console.WriteLine("Xml Before:"); - Console.WriteLine(xml.OuterXml); - - // event handler - var evented = 0; - NotificationHandler.PublishedContent = notification => - { - evented++; - - // should see the changes in context, not in master - xml = XmlInContext; - Assert.AreNotSame(beforeXml, xml); - Console.WriteLine("Xml Event:"); - Console.WriteLine(xml.OuterXml); - var node = xml.GetElementById(item.Id.ToString()); - Assert.IsNotNull(node); - - xml = XmlMaster; - Assert.AreSame(beforeXml, xml); - Assert.AreEqual(beforeOuterXml, xml.OuterXml); - }; - - using (var scope = ScopeProvider.CreateScope()) - { - ServiceContext.ContentService.SaveAndPublish(item); // should create an xml clone - item.Name = "changed"; - ServiceContext.ContentService.SaveAndPublish(item); // should re-use the xml clone - - // this should never change - Assert.AreEqual(beforeOuterXml, beforeXml.OuterXml); - - // this does not change, other thread don't see the changes - xml = XmlMaster; - Assert.AreSame(beforeXml, xml); - Console.WriteLine("XmlInternal During:"); - Console.WriteLine(xml.OuterXml); - Assert.AreEqual(beforeOuterXml, xml.OuterXml); - - // this does not change during the scope (only in events) - // because it is the events that trigger the changes - xml = XmlInContext; - Assert.IsNotNull(xml); - Assert.AreSame(beforeXml, xml); - Assert.AreEqual(beforeOuterXml, xml.OuterXml); - - // note - // this means that, as long as ppl don't create scopes, they'll see their - // changes right after SaveAndPublish, but if they create scopes, - // they will have to wail until the scope is completed, ie wait until the - // events trigger, to use eg GetUrl etc - - if (complete) - scope.Complete(); - } - - //The reason why there is only 1 event occuring is because we are publishing twice for the same event for the same - //object and the scope deduplicates the events(uses the latest) - Assert.AreEqual(complete? 1 : 0, evented); - - // this should never change - Assert.AreEqual(beforeOuterXml, beforeXml.OuterXml); - - xml = XmlInContext; - Console.WriteLine("Xml After:"); - Console.WriteLine(xml.OuterXml); - if (complete) - { - var node = xml.GetElementById(item.Id.ToString()); - Assert.IsNotNull(node); - } - else - { - Assert.AreSame(beforeXml, xml); - Assert.AreEqual(beforeOuterXml, xml.OuterXml); // nothing has changed! - } - - xml = XmlMaster; - if (complete) - { - var node = xml.GetElementById(item.Id.ToString()); - Assert.IsNotNull(node); - } - else - { - Assert.AreSame(beforeXml, xml); - Assert.AreEqual(beforeOuterXml, xml.OuterXml); // nothing has changed! - } - } - - [TestCase(true)] - [TestCase(false)] - public void TestScopeMany(bool complete) - { - var umbracoContext = GetUmbracoContext("http://example.com/", setSingleton: true); - - // sanity checks - Assert.AreSame(umbracoContext, Umbraco.Web.Composing.Current.UmbracoContext); - Assert.AreSame(XmlStore, ((PublishedContentCache)umbracoContext.Content).XmlStore); - - // create document type - var contentType = new ContentType(ShortStringHelper,-1) { Alias = "CustomDocument", Name = "Custom Document" }; - ServiceContext.ContentTypeService.Save(contentType); - - // wire cache refresher - _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(Current.ServerMessenger, Current.CacheRefreshers)); - - // check xml in context = "before" - var xml = XmlInContext; - var beforeXml = xml; - var beforeOuterXml = beforeXml.OuterXml; - var item = new Content("name", -1, contentType); - const int count = 10; - var ids = new int[count]; - var clones = 0; - - SafeXmlReaderWriter.Cloning = () => { clones++; }; - - Console.WriteLine("Xml Before:"); - Console.WriteLine(xml.OuterXml); - - using (var scope = ScopeProvider.CreateScope()) - { - ServiceContext.ContentService.SaveAndPublish(item); - - for (var i = 0; i < count; i++) - { - var temp = new Content("content_" + i, -1, contentType); - ServiceContext.ContentService.SaveAndPublish(temp); - ids[i] = temp.Id; - } - - // this should never change - Assert.AreEqual(beforeOuterXml, beforeXml.OuterXml); - - xml = XmlMaster; - Assert.IsNotNull(xml); - Console.WriteLine("Xml InScope (before complete):"); - Console.WriteLine(xml.OuterXml); - Assert.AreEqual(beforeOuterXml, xml.OuterXml); - - if (complete) - scope.Complete(); - - xml = XmlMaster; - Assert.IsNotNull(xml); - Console.WriteLine("Xml InScope (after complete):"); - Console.WriteLine(xml.OuterXml); - Assert.AreEqual(beforeOuterXml, xml.OuterXml); - } - - var scopeProvider = ScopeProvider; - Assert.IsNotNull(scopeProvider); - // ambient scope may be null, or maybe not, depending on whether the code that - // was called did proper scoped work, or some direct (NoScope) use of the database - Assert.IsNull(scopeProvider.AmbientContext); - - // limited number of clones! - Assert.AreEqual(complete ? 1 : 0, clones); - - // this should never change - Assert.AreEqual(beforeOuterXml, beforeXml.OuterXml); - - xml = XmlMaster; - Assert.IsNotNull(xml); - Console.WriteLine("Xml After:"); - Console.WriteLine(xml.OuterXml); - if (complete) - { - var node = xml.GetElementById(item.Id.ToString()); - Assert.IsNotNull(node); - - for (var i = 0; i < 10; i++) - { - node = xml.GetElementById(ids[i].ToString()); - Assert.IsNotNull(node); - } - } - else - { - Assert.AreEqual(beforeOuterXml, xml.OuterXml); // nothing has changed! - } - } - - public class LocalServerMessenger : ServerMessengerBase - { - public LocalServerMessenger() - : base(false) - { } - - public override void SendMessages() { } - - public override void Sync() { } - - protected override void DeliverRemote(ICacheRefresher refresher, MessageType messageType, IEnumerable ids = null, string json = null) - { - throw new NotImplementedException(); - } - } - } -} diff --git a/tests/Umbraco.Tests/Services/Importing/Dictionary-Package.xml b/tests/Umbraco.Tests/Services/Importing/Dictionary-Package.xml deleted file mode 100644 index 18ff588d6404..000000000000 --- a/tests/Umbraco.Tests/Services/Importing/Dictionary-Package.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - Dictionary-Package - 1.0 - MIT license - http://not.available - - 3 - 0 - 0 - - - - Test - http://not.available - - - - - - - - - - - - - - - - - - diff --git a/tests/Umbraco.Tests/Services/TestWithSomeContentBase.cs b/tests/Umbraco.Tests/Services/TestWithSomeContentBase.cs deleted file mode 100644 index 4b99f3d127d0..000000000000 --- a/tests/Umbraco.Tests/Services/TestWithSomeContentBase.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Threading; -using NUnit.Framework; -using Umbraco.Cms.Core.Models; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Entities; - -namespace Umbraco.Tests.Services -{ - [TestFixture] - public abstract class TestWithSomeContentBase : TestWithDatabaseBase - { - public override void SetUp() - { - base.SetUp(); - CreateTestData(); - } - - public virtual void CreateTestData() - { - //NOTE Maybe not the best way to create/save test data as we are using the services, which are being tested. - - //Create and Save ContentType "umbTextpage" -> 1060 - ContentType contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage", "Textpage"); - contentType.Key = new Guid("1D3A8E6E-2EA9-4CC1-B229-1AEE19821522"); - ServiceContext.FileService.SaveTemplate(contentType.DefaultTemplate); // else, FK violation on contentType! - ServiceContext.ContentTypeService.Save(contentType); - - //Create and Save Content "Homepage" based on "umbTextpage" -> 1061 - Content textpage = MockedContent.CreateSimpleContent(contentType); - textpage.Key = new Guid("B58B3AD4-62C2-4E27-B1BE-837BD7C533E0"); - ServiceContext.ContentService.Save(textpage, 0); - - //Create and Save Content "Text Page 1" based on "umbTextpage" -> 1062 - Content subpage = MockedContent.CreateSimpleContent(contentType, "Text Page 1", textpage.Id); - subpage.ContentSchedule.Add(DateTime.Now.AddMinutes(-5), null); - ServiceContext.ContentService.Save(subpage, 0); - - //Create and Save Content "Text Page 1" based on "umbTextpage" -> 1063 - Content subpage2 = MockedContent.CreateSimpleContent(contentType, "Text Page 2", textpage.Id); - ServiceContext.ContentService.Save(subpage2, 0); - - Content subpage3 = MockedContent.CreateSimpleContent(contentType, "Text Page 3", textpage.Id); - ServiceContext.ContentService.Save(subpage3, 0); - - //Create and Save Content "Text Page Deleted" based on "umbTextpage" -> 1064 - Content trashed = MockedContent.CreateSimpleContent(contentType, "Text Page Deleted", -20); - trashed.Trashed = true; - ServiceContext.ContentService.Save(trashed, 0); - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs b/tests/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs deleted file mode 100644 index d0717c1f78da..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/BaseUsingSqlCeSyntax.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.IO; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Moq; -using NPoco; -using NUnit.Framework; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.Mappers; -using Umbraco.Cms.Persistence.SqlCe; -using Umbraco.Extensions; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.TestHelpers -{ - [TestFixture] - public abstract class BaseUsingSqlCeSyntax - { - protected IMapperCollection Mappers { get; private set; } - - protected ISqlContext SqlContext { get; private set; } - - internal TestObjects TestObjects = new TestObjects(); - - protected Sql Sql() - { - return NPoco.Sql.BuilderFor(SqlContext); - } - - [SetUp] - public virtual void Initialize() - { - var services = TestHelper.GetRegister(); - - var ioHelper = TestHelper.IOHelper; - var logger = new ProfilingLogger(Mock.Of>(), Mock.Of()); - var typeFinder = TestHelper.GetTypeFinder(); - var typeLoader = new TypeLoader(typeFinder, NoAppCache.Instance, - new DirectoryInfo(ioHelper.MapPath(Constants.SystemDirectories.TempData)), - Mock.Of>(), - logger, - false); - - var composition = new UmbracoBuilder(services, Mock.Of(), TestHelper.GetMockedTypeLoader()); - - - services.AddUnique(_ => Mock.Of()); - services.AddUnique(_ => NullLoggerFactory.Instance); - services.AddUnique(_ => Mock.Of()); - services.AddUnique(typeLoader); - - composition.WithCollectionBuilder() - .AddCoreMappers(); - - services.AddUnique(_ => SqlContext); - - var factory = Current.Factory = TestHelper.CreateServiceProvider(composition); - - var pocoMappers = new NPoco.MapperCollection { new PocoMapper() }; - var pocoDataFactory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, pocoMappers).Init()); - var sqlSyntax = new SqlCeSyntaxProvider(Options.Create(new GlobalSettings())); - SqlContext = new SqlContext(sqlSyntax, DatabaseType.SQLCe, pocoDataFactory, new Lazy(() => factory.GetRequiredService())); - Mappers = factory.GetRequiredService(); - - SetUp(); - } - - public virtual void SetUp() - {} - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/BaseWebTest.cs b/tests/Umbraco.Tests/TestHelpers/BaseWebTest.cs deleted file mode 100644 index d686856bb2a5..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/BaseWebTest.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Serialization; -using Umbraco.Cms.Tests.Common; -using Umbraco.Extensions; -using Umbraco.Tests.PublishedContent; -using Umbraco.Tests.TestHelpers.Stubs; -using Umbraco.Web.Composing; - -namespace Umbraco.Tests.TestHelpers -{ - [TestFixture] - public abstract class BaseWebTest : TestWithDatabaseBase - { - protected override void Compose() - { - base.Compose(); - - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - } - - protected override void Initialize() - { - base.Initialize(); - - // need to specify a custom callback for unit tests - // AutoPublishedContentTypes generates properties automatically - - var serializer = new ConfigurationEditorJsonSerializer(); - var dataTypeService = new TestObjects.TestDataTypeService( - new DataType(new VoidEditor(DataValueEditorFactory), serializer) { Id = 1 }); - - var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeService); - var type = new AutoPublishedContentType(Guid.NewGuid(), 0, "anything", new PublishedPropertyType[] { }); - ContentTypesCache.GetPublishedContentTypeByAlias = alias => GetPublishedContentTypeByAlias(alias) ?? type; - } - - protected virtual PublishedContentType GetPublishedContentTypeByAlias(string alias) => null; - - protected override string GetXmlContent(int templateId) - { - return @" - - - - -]> - - - - - 1 - - This is some content]]> - - - - - - - - - - - - - - - - - - -"; - } - - internal PublishedRouter CreatePublishedRouter(IUmbracoContextAccessor umbracoContextAccessor, IServiceProvider container = null, ContentFinderCollection contentFinders = null) - { - var webRoutingSettings = new WebRoutingSettings(); - return CreatePublishedRouter(umbracoContextAccessor, webRoutingSettings, container ?? Factory, contentFinders); - } - - internal static PublishedRouter CreatePublishedRouter(IUmbracoContextAccessor umbracoContextAccessor, WebRoutingSettings webRoutingSettings, IServiceProvider container = null, ContentFinderCollection contentFinders = null) - => new PublishedRouter( - Microsoft.Extensions.Options.Options.Create(webRoutingSettings), - contentFinders ?? new ContentFinderCollection(Enumerable.Empty()), - new TestLastChanceFinder(), - new TestVariationContextAccessor(), - new ProfilingLogger(Mock.Of>(), Mock.Of()), - Mock.Of>(), - Mock.Of(), - Mock.Of(), - container?.GetRequiredService() ?? Current.Factory.GetRequiredService(), - container?.GetRequiredService() ?? Current.Factory.GetRequiredService(), - container?.GetRequiredService() ?? Current.Factory.GetRequiredService(), - umbracoContextAccessor, - Mock.Of() - ); - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingExtensions.cs b/tests/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingExtensions.cs deleted file mode 100644 index c812eeb41ded..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using Microsoft.Owin.Extensions; -using Owin; - -namespace Umbraco.Tests.TestHelpers.ControllerTesting -{ - public static class AuthenticateEverythingExtensions - { - public static IAppBuilder AuthenticateEverything(this IAppBuilder app) - { - if (app == null) - throw new ArgumentNullException("app"); - app.Use(typeof(AuthenticateEverythingMiddleware), (object)app, (object)new AuthenticateEverythingMiddleware.AuthenticateEverythingAuthenticationOptions()); - app.UseStageMarker(PipelineStage.Authenticate); - return app; - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs b/tests/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs deleted file mode 100644 index 96239d5921de..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/AuthenticateEverythingMiddleware.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Security.Claims; -using System.Threading.Tasks; -using Microsoft.Owin; -using Microsoft.Owin.Security; -using Microsoft.Owin.Security.Infrastructure; -using Owin; -using Umbraco.Extensions; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Security; - -namespace Umbraco.Tests.TestHelpers.ControllerTesting -{ - /// - /// Ensures there's an admin user assigned to the request - /// - public class AuthenticateEverythingMiddleware : AuthenticationMiddleware - { - public AuthenticateEverythingMiddleware(OwinMiddleware next, IAppBuilder app, AuthenticationOptions options) - : base(next, options) - { - } - - protected override AuthenticationHandler CreateHandler() - { - return new AuthenticateEverythingHandler(); - } - - public class AuthenticateEverythingHandler : AuthenticationHandler - { - protected override Task AuthenticateCoreAsync() - { - var securityStamp = Guid.NewGuid().ToString(); - - var identity = new ClaimsIdentity(); - identity.AddRequiredClaims( - Cms.Core.Constants.Security.SuperUserIdAsString, - "admin", - "Admin", - new[] { -1 }, - new[] { -1 }, - "en-US", - securityStamp, - new[] { "content", "media", "members" }, - new[] { "admin" }); - - return Task.FromResult(new AuthenticationTicket( - identity, - new AuthenticationProperties() - { - ExpiresUtc = DateTime.Now.AddDays(1) - })); - } - } - - public class AuthenticateEverythingAuthenticationOptions : AuthenticationOptions - { - public AuthenticateEverythingAuthenticationOptions() - : base("AuthenticateEverything") - { - AuthenticationMode = AuthenticationMode.Active; - } - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/SpecificAssemblyResolver.cs b/tests/Umbraco.Tests/TestHelpers/ControllerTesting/SpecificAssemblyResolver.cs deleted file mode 100644 index a31637981b1f..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/SpecificAssemblyResolver.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.Reflection; -using System.Web.Http.Dispatcher; - -namespace Umbraco.Tests.TestHelpers.ControllerTesting -{ - public class SpecificAssemblyResolver : IAssembliesResolver - { - private readonly Assembly[] _assemblies; - - public SpecificAssemblyResolver(Assembly[] assemblies) - { - _assemblies = assemblies; - } - - public ICollection GetAssemblies() - { - return _assemblies; - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs b/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs deleted file mode 100644 index 1c1687dec8a7..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivator.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Net.Http; -using System.Web.Http; -using Umbraco.Cms.Core.Web; -using Umbraco.Web; - -namespace Umbraco.Tests.TestHelpers.ControllerTesting -{ - public class TestControllerActivator : TestControllerActivatorBase - { - private readonly Func _factory; - - public TestControllerActivator(Func factory) - { - _factory = factory; - } - - protected override ApiController CreateController(Type controllerType, HttpRequestMessage msg, IUmbracoContextAccessor umbracoContextAccessor) - { - return _factory(msg, umbracoContextAccessor); - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs b/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs deleted file mode 100644 index 6e7da0a5581d..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestControllerActivatorBase.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Security.Claims; -using System.Web; -using System.Web.Http; -using System.Web.Http.Controllers; -using System.Web.Http.Dispatcher; -using Moq; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Models.Membership; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Tests.Common; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers.Entities; -using Umbraco.Web; -using Umbraco.Web.WebApi; - -namespace Umbraco.Tests.TestHelpers.ControllerTesting -{ - /// - /// Used to mock all of the services required for re-mocking and testing controllers - /// - /// - /// A more complete version of this is found in the Umbraco REST API project but this has the basics covered - /// - public abstract class TestControllerActivatorBase : DefaultHttpControllerActivator, IHttpControllerActivator - { - IHttpController IHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) - { - // default - if (!typeof (UmbracoApiControllerBase).IsAssignableFrom(controllerType)) - return base.Create(request, controllerDescriptor, controllerType); - - var owinContext = request.TryGetOwinContext().Result; - - var mockedUserService = Mock.Of(); - var mockedContentService = Mock.Of(); - var mockedMediaService = Mock.Of(); - var mockedEntityService = Mock.Of(); - var mockedMemberService = Mock.Of(); - var mockedMemberTypeService = Mock.Of(); - var mockedDataTypeService = Mock.Of(); - var mockedContentTypeService = Mock.Of(); - - var serviceContext = ServiceContext.CreatePartial( - userService: mockedUserService, - contentService: mockedContentService, - mediaService: mockedMediaService, - entityService: mockedEntityService, - memberService: mockedMemberService, - memberTypeService: mockedMemberTypeService, - dataTypeService: mockedDataTypeService, - contentTypeService: mockedContentTypeService, - localizedTextService:Mock.Of()); - - var globalSettings = new GlobalSettings(); - - // FIXME: v8? - ////new app context - //var dbCtx = new Mock(Mock.Of(), Mock.Of(), Mock.Of(), "test"); - ////ensure these are set so that the appctx is 'Configured' - //dbCtx.Setup(x => x.CanConnect).Returns(true); - //dbCtx.Setup(x => x.IsDatabaseConfigured).Returns(true); - //var appCtx = ApplicationContext.EnsureContext( - // dbCtx.Object, - // //pass in mocked services - // serviceContext, - // CacheHelper.CreateDisabledCacheHelper(), - // new ProfilingLogger(Mock.Of(), Mock.Of()), - // true); - - var httpContextItems = new Dictionary - { - //add the special owin environment to the httpcontext items, this is how the GetOwinContext works - ["owin.Environment"] = new Dictionary() - }; - - //httpcontext with an auth'd user - var httpContext = Mock.Of( - http => http.User == owinContext.Authentication.User - //ensure the request exists with a cookies collection - && http.Request == Mock.Of(r => r.Cookies == new HttpCookieCollection() - && r.RequestContext == new System.Web.Routing.RequestContext - { - RouteData = new System.Web.Routing.RouteData() - }) - //ensure the request exists with an items collection - && http.Items == httpContextItems); - //chuck it into the props since this is what MS does when hosted and it's needed there - request.Properties["MS_HttpContext"] = httpContext; - - var backofficeIdentity = (ClaimsIdentity) owinContext.Authentication.User.Identity; - - var backofficeSecurity = new Mock(); - - //mock CurrentUser - var groups = new List(); - for (var index = 0; index < backofficeIdentity.GetRoles().Length; index++) - { - var role = backofficeIdentity.GetRoles()[index]; - groups.Add(new ReadOnlyUserGroup(index + 1, role, "icon-user", null, null, role, new string[0], new string[0])); - } - var mockUser = MockedUser.GetUserMock(); - mockUser.Setup(x => x.IsApproved).Returns(true); - mockUser.Setup(x => x.IsLockedOut).Returns(false); - mockUser.Setup(x => x.AllowedSections).Returns(backofficeIdentity.GetAllowedApplications()); - mockUser.Setup(x => x.Groups).Returns(groups); - mockUser.Setup(x => x.Email).Returns("admin@admin.com"); - mockUser.Setup(x => x.Id).Returns((int)backofficeIdentity.GetId()); - mockUser.Setup(x => x.Language).Returns("en"); - mockUser.Setup(x => x.Name).Returns(backofficeIdentity.GetRealName()); - mockUser.Setup(x => x.StartContentIds).Returns(backofficeIdentity.GetStartContentNodes()); - mockUser.Setup(x => x.StartMediaIds).Returns(backofficeIdentity.GetStartMediaNodes()); - mockUser.Setup(x => x.Username).Returns(backofficeIdentity.GetUsername()); - backofficeSecurity.Setup(x => x.CurrentUser) - .Returns(mockUser.Object); - - //mock Validate - backofficeSecurity.Setup(x => x.UserHasSectionAccess(It.IsAny(), It.IsAny())) - .Returns(() => true); - - var publishedSnapshot = new Mock(); - publishedSnapshot.Setup(x => x.Members).Returns(Mock.Of()); - var publishedSnapshotService = new Mock(); - publishedSnapshotService.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot.Object); - - var umbracoContextAccessor = Umbraco.Web.Composing.Current.UmbracoContextAccessor; - - var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext); - var umbCtx = new UmbracoContext(httpContextAccessor, - publishedSnapshotService.Object, - backofficeSecurity.Object, - globalSettings, - TestHelper.GetHostingEnvironment(), - new TestVariationContextAccessor(), - TestHelper.UriUtility, - new AspNetCookieManager(httpContextAccessor)); - - //replace it - umbracoContextAccessor.UmbracoContext = umbCtx; - - var urlHelper = new Mock(); - urlHelper.Setup(provider => provider.GetUrl(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(UrlInfo.Url("/hello/world/1234")); - - return CreateController(controllerType, request, umbracoContextAccessor); - } - - protected abstract ApiController CreateController(Type controllerType, HttpRequestMessage msg, IUmbracoContextAccessor umbracoContextAccessor); - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs b/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs deleted file mode 100644 index 408046ad9a63..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TestStartup.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Net.Http; -using System.Web.Http; -using System.Web.Http.Dispatcher; -using System.Web.Http.ExceptionHandling; -using System.Web.Http.Tracing; -using Owin; -using Umbraco.Cms.Core.Web; -using Umbraco.Web; -using Umbraco.Web.Hosting; -using Umbraco.Web.WebApi; - -namespace Umbraco.Tests.TestHelpers.ControllerTesting -{ - /// - /// Startup class for the self-hosted web server works for OWIN and WebAPI - /// - public class TestStartup - { - private readonly Func _controllerFactory; - private readonly Action _initialize; - - public TestStartup(Action initialize, Func controllerFactory) - { - _controllerFactory = controllerFactory; - _initialize = initialize; - } - - public void Configuration(IAppBuilder app) - { - var httpConfig = new HttpConfiguration(); - - // TODO: Enable this if you can't see the errors produced - // var traceWriter = httpConfig.EnableSystemDiagnosticsTracing(); - // traceWriter.IsVerbose = true; - // traceWriter.MinimumLevel = TraceLevel.Debug; - - httpConfig.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; - - // Add in a simple exception tracer so we can see what is causing the 500 Internal Server Error - httpConfig.Services.Add(typeof (IExceptionLogger), new TraceExceptionLogger()); - - httpConfig.Services.Replace(typeof (IAssembliesResolver), new SpecificAssemblyResolver(new[] { typeof (AspNetApplicationShutdownRegistry).Assembly })); - httpConfig.Services.Replace(typeof (IHttpControllerActivator), new TestControllerActivator(_controllerFactory)); - httpConfig.Services.Replace(typeof (IHttpControllerSelector), new NamespaceHttpControllerSelector(httpConfig)); - - //auth everything - app.AuthenticateEverything(); - - _initialize(httpConfig); - - app.UseWebApi(httpConfig); - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TraceExceptionLogger.cs b/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TraceExceptionLogger.cs deleted file mode 100644 index de7bd8fc10c3..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/ControllerTesting/TraceExceptionLogger.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Diagnostics; -using System.Web.Http.ExceptionHandling; - -namespace Umbraco.Tests.TestHelpers.ControllerTesting -{ - /// - /// Traces any errors for WebApi to the output window - /// - public class TraceExceptionLogger : ExceptionLogger - { - public override void Log(ExceptionLoggerContext context) - { - Trace.TraceError(context.ExceptionContext.Exception.ToString()); - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs b/tests/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs deleted file mode 100644 index 2aba18cfe6e7..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Entities/MockedContent.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Tests.Common.Extensions; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.TestHelpers.Entities -{ - public class MockedContent - { - public static Content CreateBasicContent(IContentType contentType) - { - var content = new Content("Home", -1, contentType) { Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 }; - - content.ResetDirtyProperties(false); - - return content; - } - - public static Content CreateSimpleContent(IContentType contentType) - { - var content = new Content("Home", -1, contentType) { Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 }; - object obj = - new - { - title = "Welcome to our Home page", - bodyText = "This is the welcome message on the first page", - author = "John Doe" - }; - - content.PropertyValues(obj); - - content.ResetDirtyProperties(false); - - return content; - } - - public static Content CreateSimpleContent(IContentType contentType, string name, int parentId = -1, string culture = null, string segment = null) - { - var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0 }; - object obj = - new - { - title = name + " Subpage", - bodyText = "This is a subpage", - author = "John Doe" - }; - - content.PropertyValues(obj, culture, segment); - - content.ResetDirtyProperties(false); - - return content; - } - - public static Content CreateSimpleContent(IContentType contentType, string name, IContent parent, string culture = null, string segment = null, bool setPropertyValues = true) - { - var content = new Content(name, parent, contentType, culture) { CreatorId = 0, WriterId = 0 }; - - if (setPropertyValues) - { - object obj = - new - { - title = name + " Subpage", - bodyText = "This is a subpage", - author = "John Doe" - }; - - content.PropertyValues(obj, culture, segment); - } - - content.ResetDirtyProperties(false); - - return content; - } - - public static Content CreateTextpageContent(IContentType contentType, string name, int parentId) - { - var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0}; - object obj = - new - { - title = name + " textpage", - bodyText = string.Format("This is a textpage based on the {0} ContentType", contentType.Alias), - keywords = "text,page,meta", - description = "This is the meta description for a textpage" - }; - - content.PropertyValues(obj); - - content.ResetDirtyProperties(false); - - return content; - } - - public static Content CreateSimpleContentWithSpecialDatabaseTypes(IContentType contentType, string name, int parentId, string decimalValue, string intValue, DateTime datetimeValue) - { - var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0 }; - object obj = new - { - decimalProperty = decimalValue, - intProperty = intValue, - datetimeProperty = datetimeValue - }; - - content.PropertyValues(obj); - content.ResetDirtyProperties(false); - return content; - } - - public static Content CreateAllTypesContent(IContentType contentType, string name, int parentId) - { - var content = new Content("Random Content Name", parentId, contentType) { Level = 1, SortOrder = 1, CreatorId = 0, WriterId = 0 }; - - content.SetValue("isTrue", true); - content.SetValue("number", 42); - content.SetValue("bodyText", "Lorem Ipsum Body Text Test"); - content.SetValue("singleLineText", "Single Line Text Test"); - content.SetValue("multilineText", "Multiple lines \n in one box"); - content.SetValue("upload", "/media/1234/koala.jpg"); - content.SetValue("label", "Non-editable label"); - content.SetValue("dateTime", DateTime.Now.AddDays(-20)); - content.SetValue("colorPicker", "black"); - content.SetValue("ddlMultiple", "1234,1235"); - content.SetValue("rbList", "random"); - content.SetValue("date", DateTime.Now.AddDays(-10)); - content.SetValue("ddl", "1234"); - content.SetValue("chklist", "randomc"); - content.SetValue("contentPicker", Udi.Create(Constants.UdiEntityType.Document, new Guid("74ECA1D4-934E-436A-A7C7-36CC16D4095C")).ToString()); - content.SetValue("mediaPicker3", "[{\"key\": \"8f78ce9e-8fe0-4500-a52d-4c4f35566ba9\",\"mediaKey\": \"44CB39C8-01E5-45EB-9CF8-E70AAF2D1691\",\"crops\": [],\"focalPoint\": {\"left\": 0.5,\"top\": 0.5}}]"); - content.SetValue("memberPicker", Udi.Create(Constants.UdiEntityType.Member, new Guid("9A50A448-59C0-4D42-8F93-4F1D55B0F47D")).ToString()); - content.SetValue("multiUrlPicker", "[{\"name\":\"https://test.com\",\"url\":\"https://test.com\"}]"); - content.SetValue("tags", "this,is,tags"); - - return content; - } - - public static IEnumerable CreateTextpageContent(IContentType contentType, int parentId, int amount) - { - var list = new List(); - - for (int i = 0; i < amount; i++) - { - var name = "Textpage No-" + i; - var content = new Content(name, parentId, contentType) { CreatorId = 0, WriterId = 0 }; - object obj = - new - { - title = name + " title", - bodyText = string.Format("This is a textpage based on the {0} ContentType", contentType.Alias), - keywords = "text,page,meta", - description = "This is the meta description for a textpage" - }; - - content.PropertyValues(obj); - - content.ResetDirtyProperties(false); - - list.Add(content); - } - - return list; - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs b/tests/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs deleted file mode 100644 index 0f0a57c9eb9e..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Entities/MockedContentTypes.cs +++ /dev/null @@ -1,514 +0,0 @@ -using System; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Strings; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.TestHelpers.Entities -{ - public class MockedContentTypes - { - public static IShortStringHelper ShortStringHelper { get; } = - new DefaultShortStringHelper(new DefaultShortStringHelperConfig()); - - public static ContentType CreateBasicContentType(string alias = "basePage", string name = "Base Page", IContentType parent = null) - { - var contentType = parent == null ? new ContentType(ShortStringHelper, -1) : new ContentType(ShortStringHelper, parent, alias); - - contentType.Alias = alias; - contentType.Name = name; - contentType.Description = "ContentType used for basic pages"; - contentType.Icon = ".sprTreeDoc3"; - contentType.Thumbnail = "doc2.png"; - contentType.SortOrder = 1; - contentType.CreatorId = 0; - contentType.Trashed = false; - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - return contentType; - } - - public static ContentType CreateTextPageContentType(string alias = "textPage", string name = "Text Page") - { - var contentType = new ContentType(ShortStringHelper, -1) - { - Alias = alias, - Name = name, - Description = "ContentType used for Text pages", - Icon = ".sprTreeDoc3", - Thumbnail = "doc.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - var contentCollection = new PropertyTypeCollection(true); - contentCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = Constants.DataTypes.Textbox, LabelOnTop = true }); - contentCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.DataTypes.RichtextEditor, LabelOnTop = false }); - - var metaCollection = new PropertyTypeCollection(true); - metaCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "keywords", Name = "Meta Keywords", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = Constants.DataTypes.Textbox }); - metaCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "description", Name = "Meta Description", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.DataTypes.Textarea }); - - contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); - contentType.PropertyGroups.Add(new PropertyGroup(metaCollection) { Name = "Meta", SortOrder = 2 }); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - contentType.SetDefaultTemplate(new Template(ShortStringHelper, "Textpage", "textpage")); - - return contentType; - } - - public static ContentType CreateMetaContentType() - { - var contentType = new ContentType(ShortStringHelper, -1) - { - Alias = "meta", - Name = "Meta", - Description = "ContentType used for Meta tags", - Icon = ".sprTreeDoc3", - Thumbnail = "doc.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - var metaCollection = new PropertyTypeCollection(true); - metaCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "metakeywords", Name = "Meta Keywords", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - metaCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "metadescription", Name = "Meta Description", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -89 }); - - contentType.PropertyGroups.Add(new PropertyGroup(metaCollection) { Name = "Meta", SortOrder = 2 }); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - return contentType; - } - - public static ContentType CreateContentMetaContentType() - { - var contentType = new ContentType(ShortStringHelper, -1) - { - Alias = "contentMeta", - Name = "Content Meta", - Description = "ContentType used for Content Meta", - Icon = ".sprTreeDoc3", - Thumbnail = "doc.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - var metaCollection = new PropertyTypeCollection(true); - metaCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - - contentType.PropertyGroups.Add(new PropertyGroup(metaCollection) { Name = "Content", SortOrder = 2 }); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - return contentType; - } - - public static ContentType CreateSeoContentType() - { - var contentType = new ContentType(ShortStringHelper, -1) - { - Alias = "seo", - Name = "Seo", - Description = "ContentType used for Seo", - Icon = ".sprTreeDoc3", - Thumbnail = "doc.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - var metaCollection = new PropertyTypeCollection(true); - metaCollection.Add(new PropertyType(ShortStringHelper, "seotest", ValueStorageType.Ntext) { Alias = "seokeywords", Name = "Seo Keywords", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - metaCollection.Add(new PropertyType(ShortStringHelper, "seotest", ValueStorageType.Ntext) { Alias = "seodescription", Name = "Seo Description", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -89 }); - - contentType.PropertyGroups.Add(new PropertyGroup(metaCollection) { Name = "Seo", SortOrder = 5 }); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - return contentType; - } - - public static ContentType CreateSimpleContentType() - { - var contentType = new ContentType(ShortStringHelper, -1) - { - Alias = "simple", - Name = "Simple Page", - Description = "ContentType used for simple text pages", - Icon = ".sprTreeDoc3", - Thumbnail = "doc.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - var contentCollection = new PropertyTypeCollection(true); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TinyMce, ValueStorageType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -87 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", Mandatory = false, SortOrder = 3, DataTypeId = -88 }); - - contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) - { - Name = "Content", - Alias = "content", - SortOrder = 1 - }); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - return contentType; - } - - public static ContentType CreateSimpleContentType3(string alias, string name, IContentType parent = null, bool randomizeAliases = false, string propertyGroupName = "Content") - { - var contentType = CreateSimpleContentType(alias, name, parent, randomizeAliases, propertyGroupName); - - var propertyType = new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Tags, ValueStorageType.Nvarchar) - { - Alias = RandomAlias("tags", randomizeAliases), - Name = "Tags", - Description = "Tags", - Mandatory = false, - SortOrder = 99, - DataTypeId = Constants.DataTypes.Tags - }; - contentType.AddPropertyType(propertyType); - - return contentType; - } - - - - public static ContentType CreateSimpleContentType(string alias, string name, IContentType parent = null, bool randomizeAliases = false, string propertyGroupName = "Content") - { - var contentType = parent == null ? new ContentType(ShortStringHelper, -1) : new ContentType(ShortStringHelper, parent, alias); - - contentType.Alias = alias; - contentType.Name = name; - contentType.Description = "ContentType used for simple text pages"; - contentType.Icon = ".sprTreeDoc3"; - contentType.Thumbnail = "doc2.png"; - contentType.SortOrder = 1; - contentType.CreatorId = 0; - contentType.Trashed = false; - - var contentCollection = new PropertyTypeCollection(true); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext) { Alias = RandomAlias("title", randomizeAliases), Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88, LabelOnTop = true }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TinyMce, ValueStorageType.Ntext) { Alias = RandomAlias("bodyText", randomizeAliases), Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -87 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext) { Alias = RandomAlias("author", randomizeAliases) , Name = "Author", Description = "Name of the author", Mandatory = false, SortOrder = 3, DataTypeId = -88 }); - - var pg = new PropertyGroup(contentCollection) - { - Name = propertyGroupName, - SortOrder = 1 - }; - contentType.PropertyGroups.Add(pg); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - contentType.SetDefaultTemplate(new Template(ShortStringHelper, "Textpage", "textpage")); - - return contentType; - } - - public static MediaType CreateSimpleMediaType(string alias, string name, IMediaType parent = null, bool randomizeAliases = false, string propertyGroupName = "Content") - { - var contentType = parent == null ? new MediaType(ShortStringHelper, -1) : new MediaType(ShortStringHelper, parent, alias); - - contentType.Alias = alias; - contentType.Name = name; - contentType.Description = "ContentType used for simple text pages"; - contentType.Icon = ".sprTreeDoc3"; - contentType.Thumbnail = "doc2.png"; - contentType.SortOrder = 1; - contentType.CreatorId = 0; - contentType.Trashed = false; - - var contentCollection = new PropertyTypeCollection(false); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext) { Alias = RandomAlias("title", randomizeAliases), Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TinyMce, ValueStorageType.Ntext) { Alias = RandomAlias("bodyText", randomizeAliases), Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -87 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext) { Alias = RandomAlias("author", randomizeAliases), Name = "Author", Description = "Name of the author", Mandatory = false, SortOrder = 3, DataTypeId = -88 }); - - var pg = new PropertyGroup(contentCollection) { Name = propertyGroupName, SortOrder = 1 }; - contentType.PropertyGroups.Add(pg); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - return contentType; - } - - public static ContentType CreateSimpleContentType(string alias, string name, bool mandatory) - { - var contentType = new ContentType(ShortStringHelper, -1) - { - Alias = alias, - Name = name, - Description = "ContentType used for simple text pages", - Icon = ".sprTreeDoc3", - Thumbnail = "doc2.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - var contentCollection = new PropertyTypeCollection(true); - contentCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = mandatory, SortOrder = 1, DataTypeId = -88 }); - contentCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", Mandatory = mandatory, SortOrder = 2, DataTypeId = -87 }); - contentCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", Mandatory = mandatory, SortOrder = 3, DataTypeId = -88 }); - - contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - return contentType; - } - - public static ContentType CreateSimpleContentType(string alias, string name, PropertyTypeCollection collection) - { - var contentType = new ContentType(ShortStringHelper, -1) - { - Alias = alias, - Name = name, - Description = "ContentType used for simple text pages", - Icon = ".sprTreeDoc3", - Thumbnail = "doc3.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - contentType.PropertyGroups.Add(new PropertyGroup(collection) { Name = "Content", SortOrder = 1 }); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - return contentType; - } - - public static ContentType CreateSimpleContentType(string alias, string name, PropertyTypeCollection collection, string propertyGroupName, IContentType parent = null) - { - var contentType = parent == null ? new ContentType(ShortStringHelper, -1) : new ContentType(ShortStringHelper, parent, alias); - - contentType.Alias = alias; - contentType.Name = name; - contentType.Description = "ContentType used for simple text pages"; - contentType.Icon = ".sprTreeDoc3"; - contentType.Thumbnail = "doc2.png"; - contentType.SortOrder = 1; - contentType.CreatorId = 0; - contentType.Trashed = false; - - contentType.PropertyGroups.Add(new PropertyGroup(collection) { Name = propertyGroupName, SortOrder = 1 }); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - return contentType; - } - - public static ContentType CreateSimpleContentType(string alias, string name, PropertyTypeCollection groupedCollection, PropertyTypeCollection nonGroupedCollection) - { - var contentType = CreateSimpleContentType(alias, name, groupedCollection); - //now add the non-grouped properties - foreach (var x in nonGroupedCollection) - contentType.AddPropertyType(x); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - return contentType; - } - - public static ContentType CreateAllTypesContentType(string alias, string name) - { - var contentType = new ContentType(ShortStringHelper, -1) - { - Alias = alias, - Name = name, - Description = "ContentType containing all standard DataTypes", - Icon = ".sprTreeDoc3", - Thumbnail = "doc.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - var contentCollection = new PropertyTypeCollection(true); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Boolean, ValueStorageType.Integer) { Alias = "isTrue", Name = "Is True or False", Mandatory = false, SortOrder = 1, DataTypeId = -49 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Integer, ValueStorageType.Integer) { Alias = "number", Name = "Number", Mandatory = false, SortOrder = 2, DataTypeId = -51 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TinyMce, ValueStorageType.Ntext) { Alias = "bodyText", Name = "Body Text", Mandatory = false, SortOrder = 3, DataTypeId = -87 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Nvarchar) { Alias = "singleLineText", Name = "Text String", Mandatory = false, SortOrder = 4, DataTypeId = -88 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextArea, ValueStorageType.Ntext) { Alias = "multilineText", Name = "Multiple Text Strings", Mandatory = false, SortOrder = 5, DataTypeId = -89 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.UploadField, ValueStorageType.Nvarchar) { Alias = "upload", Name = "Upload Field", Mandatory = false, SortOrder = 6, DataTypeId = -90 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = "label", Name = "Label", Mandatory = false, SortOrder = 7, DataTypeId = -92 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.DateTime, ValueStorageType.Date) { Alias = "dateTime", Name = "Date Time", Mandatory = false, SortOrder = 8, DataTypeId = -36 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.ColorPicker, ValueStorageType.Nvarchar) { Alias = "colorPicker", Name = "Color Picker", Mandatory = false, SortOrder = 9, DataTypeId = -37 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.DropDownListFlexible, ValueStorageType.Nvarchar) { Alias = "ddlMultiple", Name = "Dropdown List Multiple", Mandatory = false, SortOrder = 11, DataTypeId = -39 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.RadioButtonList, ValueStorageType.Nvarchar) { Alias = "rbList", Name = "Radio Button List", Mandatory = false, SortOrder = 12, DataTypeId = -40 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.DateTime, ValueStorageType.Date) { Alias = "date", Name = "Date", Mandatory = false, SortOrder = 13, DataTypeId = -36 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.DropDownListFlexible, ValueStorageType.Integer) { Alias = "ddl", Name = "Dropdown List", Mandatory = false, SortOrder = 14, DataTypeId = -42 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.CheckBoxList, ValueStorageType.Nvarchar) { Alias = "chklist", Name = "Checkbox List", Mandatory = false, SortOrder = 15, DataTypeId = -43 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.ContentPicker, ValueStorageType.Integer) { Alias = "contentPicker", Name = "Content Picker", Mandatory = false, SortOrder = 16, DataTypeId = 1046 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.MediaPicker3, ValueStorageType.Integer) { Alias = "mediapicker3", Name = "Media Picker", Mandatory = false, SortOrder = 17, DataTypeId = 1051 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.MemberPicker, ValueStorageType.Integer) { Alias = "memberPicker", Name = "Member Picker", Mandatory = false, SortOrder = 18, DataTypeId = 1047 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.MultiUrlPicker, ValueStorageType.Nvarchar) { Alias = "multiUrlPicker", Name = "Multi URL Picker", Mandatory = false, SortOrder = 21, DataTypeId = 1050 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Tags, ValueStorageType.Ntext) { Alias = "tags", Name = "Tags", Mandatory = false, SortOrder = 22, DataTypeId = 1041 }); - - contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); - - return contentType; - } - - public static MediaType CreateNewMediaType() - { - var mediaType = new MediaType(ShortStringHelper, -1) - { - Alias = "newMediaType", - Name = "New Media Type", - Description = "ContentType used for a new format", - Icon = ".sprTreeDoc3", - Thumbnail = "doc.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - var contentCollection = new PropertyTypeCollection(false); - contentCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - contentCollection.Add(new PropertyType(ShortStringHelper, "test", ValueStorageType.Nvarchar) { Alias = "videoFile", Name = "Video File", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -90 }); - - mediaType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Media", SortOrder = 1 }); - - //ensure that nothing is marked as dirty - mediaType.ResetDirtyProperties(false); - - return mediaType; - } - - public static MediaType CreateImageMediaType(string alias = Constants.Conventions.MediaTypes.Image) - { - var mediaType = new MediaType(ShortStringHelper, -1) - { - Alias = alias, - Name = "Image", - Description = "ContentType used for images", - Icon = ".sprTreeDoc3", - Thumbnail = "doc.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - var contentCollection = new PropertyTypeCollection(false); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.UploadField, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.File, Name = "File", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -90 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); - - mediaType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Media", SortOrder = 1 }); - - //ensure that nothing is marked as dirty - mediaType.ResetDirtyProperties(false); - - return mediaType; - } - - public static MediaType CreateImageMediaTypeWithCrop(string alias = Constants.Conventions.MediaTypes.Image) - { - var mediaType = new MediaType(ShortStringHelper, -1) - { - Alias = alias, - Name = "Image", - Description = "ContentType used for images", - Icon = ".sprTreeDoc3", - Thumbnail = "doc.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - var contentCollection = new PropertyTypeCollection(false); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.ImageCropper, ValueStorageType.Ntext) { Alias = Constants.Conventions.Media.File, Name = "File", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = 1043 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Width, Name = "Width", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Height, Name = "Height", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Integer) { Alias = Constants.Conventions.Media.Bytes, Name = "Bytes", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar) { Alias = Constants.Conventions.Media.Extension, Name = "File Extension", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = Constants.System.DefaultLabelDataTypeId }); - - mediaType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Media", SortOrder = 1 }); - - //ensure that nothing is marked as dirty - mediaType.ResetDirtyProperties(false); - - return mediaType; - } - - public static MemberType CreateSimpleMemberType(string alias = null, string name = null) - { - var contentType = new MemberType(ShortStringHelper, -1) - { - Alias = alias ?? "simple", - Name = name ?? "Simple Page", - Description = "Some member type", - Icon = ".sprTreeDoc3", - Thumbnail = "doc.png", - SortOrder = 1, - CreatorId = 0, - Trashed = false - }; - - var contentCollection = new PropertyTypeCollection(false); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext) { Alias = "title", Name = "Title", Description = "", Mandatory = false, SortOrder = 1, DataTypeId = -88 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext) { Alias = "bodyText", Name = "Body Text", Description = "", Mandatory = false, SortOrder = 2, DataTypeId = -87 }); - contentCollection.Add(new PropertyType(ShortStringHelper, Constants.PropertyEditors.Aliases.TextBox, ValueStorageType.Ntext) { Alias = "author", Name = "Author", Description = "Name of the author", Mandatory = false, SortOrder = 3, DataTypeId = -88 }); - - contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); - - //ensure that nothing is marked as dirty - contentType.ResetDirtyProperties(false); - - return contentType; - } - - public static void EnsureAllIds(ContentTypeCompositionBase contentType, int seedId) - { - //ensure everything has ids - contentType.Id = seedId; - var itemid = seedId + 1; - foreach (var propertyGroup in contentType.PropertyGroups) - { - propertyGroup.Id = itemid++; - } - foreach (var propertyType in contentType.PropertyTypes) - { - propertyType.Id = itemid++; - } - } - - - private static string RandomAlias(string alias, bool randomizeAliases) - { - if (randomizeAliases) - { - return string.Concat(alias, Guid.NewGuid().ToString("N")); - } - - return alias; - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Entities/MockedEntity.cs b/tests/Umbraco.Tests/TestHelpers/Entities/MockedEntity.cs deleted file mode 100644 index c40d5b33bf8d..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Entities/MockedEntity.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Runtime.Serialization; -using Umbraco.Cms.Core.Models.Entities; - -namespace Umbraco.Tests.TestHelpers.Entities -{ - [Serializable] - [DataContract(IsReference = true)] - public class MockedEntity : EntityBase - { - [DataMember] - public string Alias { get; set; } - - [DataMember] - public string Name { get; set; } - - [DataMember] - public string Value { get; set; } - } - - public class CustomMockedEntity : MockedEntity - { - [DataMember] - public string Title { get; set; } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs b/tests/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs deleted file mode 100644 index 2d8bcc63951a..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Entities/MockedMedia.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Tests.Common.Extensions; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.TestHelpers.Entities -{ - public static class MockedMedia - { - public static Media CreateSimpleMedia(IMediaType contentType, string name, int parentId) - { - var content = new Media(name, parentId, contentType) { CreatorId = 0 }; - object obj = - new - { - title = name + " Subpage", - bodyText = "This is a subpage", - author = "John Doe" - }; - - content.PropertyValues(obj); - - content.ResetDirtyProperties(false); - - return content; - } - - public static Media CreateMediaImage(IMediaType mediaType, int parentId) - { - var media = new Media("Test Image", parentId, mediaType) - { - CreatorId = 0 - }; - - media.SetValue(Constants.Conventions.Media.File, "/media/test-image.png"); - media.SetValue(Constants.Conventions.Media.Width, "200"); - media.SetValue(Constants.Conventions.Media.Height, "200"); - media.SetValue(Constants.Conventions.Media.Bytes, "100"); - media.SetValue(Constants.Conventions.Media.Extension, "png"); - - return media; - } - - public static Media CreateMediaFile(IMediaType mediaType, int parentId) - { - var media = new Media("Test File", parentId, mediaType) - { - CreatorId = 0 - }; - - media.SetValue(Constants.Conventions.Media.File, "/media/test-file.txt"); - media.SetValue(Constants.Conventions.Media.Bytes, "4"); - media.SetValue(Constants.Conventions.Media.Extension, "txt"); - - return media; - } - - public static Media CreateMediaImageWithCrop(IMediaType mediaType, int parentId) - { - var media = new Media("Test Image", parentId, mediaType) - { - CreatorId = 0 - }; - - media.SetValue(Constants.Conventions.Media.File, "{src: '/media/test-image.png', crops: []}"); - media.SetValue(Constants.Conventions.Media.Width, "200"); - media.SetValue(Constants.Conventions.Media.Height, "200"); - media.SetValue(Constants.Conventions.Media.Bytes, "100"); - media.SetValue(Constants.Conventions.Media.Extension, "png"); - - - - return media; - } - - public static Media CreateMediaFolder(IMediaType mediaType, int parentId) - { - var media = new Media("Test Folder", parentId, mediaType) - { - CreatorId = 0 - }; - - return media; - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Entities/MockedMember.cs b/tests/Umbraco.Tests/TestHelpers/Entities/MockedMember.cs deleted file mode 100644 index bc9dcf175097..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Entities/MockedMember.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Tests.TestHelpers.Entities -{ - public class MockedMember - { - public static Member CreateSimpleMember(IMemberType contentType, string name, string email, string password, string username, Guid? key = null) - { - var member = new Member(name, email, username, password, contentType) - { - CreatorId = 0, - Email = email, - RawPasswordValue = password, - Username = username - }; - - if (key.HasValue) - { - member.Key = key.Value; - } - - member.SetValue("title", name + " member"); - member.SetValue("bodyText", "This is a subpage"); - member.SetValue("author", "John Doe"); - - member.ResetDirtyProperties(false); - - return member; - } - - public static Member CreateSimpleMember(IMemberType contentType, string name, string email, string username, Guid? key = null) - { - var member = new Member(name, email, username, contentType) - { - CreatorId = 0, - Email = email, - Username = username - }; - - if (key.HasValue) - { - member.Key = key.Value; - } - - member.SetValue("title", name + " member"); - member.SetValue("bodyText", "This is a subpage"); - member.SetValue("author", "John Doe"); - - member.ResetDirtyProperties(false); - - return member; - } - - public static IEnumerable CreateSimpleMember(IMemberType memberType, int amount, Action onCreating = null) - { - var list = new List(); - - for (int i = 0; i < amount; i++) - { - var name = "Member No-" + i; - var member = new Member(name, "test" + i + "@test.com", "test" + i, "test" + i, memberType); - member.SetValue("title", name + " member" + i); - member.SetValue("bodyText", "This is a subpage" + i); - member.SetValue("author", "John Doe" + i); - - if (onCreating != null) - { - onCreating(i, member); - } - - member.ResetDirtyProperties(false); - - list.Add(member); - } - - return list; - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Entities/MockedPropertyTypes.cs b/tests/Umbraco.Tests/TestHelpers/Entities/MockedPropertyTypes.cs deleted file mode 100644 index a2d137800e41..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Entities/MockedPropertyTypes.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Umbraco.Cms.Core.Models; - -namespace Umbraco.Tests.TestHelpers.Entities -{ - public class MockedPropertyTypes - { - /// - /// Returns a decimal property. - /// Requires a datatype definition Id, since this is not one of the pre-populated datatypes - /// - /// Alias of the created property type - /// Name of the created property type - /// Integer Id of a decimal datatype to use - /// Property type storing decimal value - public static PropertyType CreateDecimalProperty(string alias, string name, int dtdId) - { - return - new PropertyType(TestHelper.ShortStringHelper, "test", ValueStorageType.Decimal, alias) - { - Name = name, - Description = "Decimal property type", - Mandatory = false, - SortOrder = 4, - DataTypeId = dtdId - }; - } - - /// - /// Returns a integer property. - /// - /// Alias of the created property type - /// Name of the created property type - /// Property type storing integer value - public static PropertyType CreateIntegerProperty(string alias, string name) - { - return - new PropertyType(TestHelper.ShortStringHelper, "test", ValueStorageType.Integer, alias) - { - Name = name, - Description = "Integer property type", - Mandatory = false, - SortOrder = 4, - DataTypeId = -51 - }; - } - - /// - /// Returns a DateTime property. - /// - /// Alias of the created property type - /// Name of the created property type - /// Property type storing DateTime value - public static PropertyType CreateDateTimeProperty(string alias, string name) - { - return - new PropertyType(TestHelper.ShortStringHelper, "test", ValueStorageType.Date, alias) - { - Name = name, - Description = "DateTime property type", - Mandatory = false, - SortOrder = 4, - DataTypeId = -36 - }; - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs b/tests/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs deleted file mode 100644 index f2fbaca57168..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Entities/MockedUser.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using Moq; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Models.Membership; - -namespace Umbraco.Tests.TestHelpers.Entities -{ - public class MockedUser - { - /// - /// Returns a and ensures that the ToUserCache and FromUserCache methods are mapped correctly for - /// dealing with start node caches - /// - /// - internal static Mock GetUserMock() - { - var userCache = new Dictionary(); - var userMock = new Mock(); - userMock.Setup(x => x.FromUserCache(It.IsAny())).Returns((string key) => userCache.TryGetValue(key, out var val) ? val is int[] iVal ? iVal : null : null); - userMock.Setup(x => x.ToUserCache(It.IsAny(), It.IsAny())).Callback((string key, int[] val) => userCache[key] = val); - return userMock; - } - - internal static User CreateUser(string suffix = "") - { - var globalSettings = new GlobalSettings(); - var user = new User(globalSettings) - { - Language = "en", - IsApproved = true, - Name = "TestUser" + suffix, - RawPasswordValue = "testing", - IsLockedOut = false, - Email = "test" + suffix + "@test.com", - Username = "TestUser" + suffix - }; - - return user; - } - - internal static IEnumerable CreateMulipleUsers(int amount, Action onCreating = null) - { - var list = new List(); - - var globalSettings = new GlobalSettings(); - for (int i = 0; i < amount; i++) - { - var name = "Member No-" + i; - var user = new User(globalSettings, name, "test" + i + "@test.com", "test" + i, "test" + i); - - onCreating?.Invoke(i, user); - - user.ResetDirtyProperties(false); - - list.Add(user); - } - - return list; - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs b/tests/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs deleted file mode 100644 index 37a510a0e8ad..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Entities/MockedUserGroup.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Umbraco.Cms.Core.Models.Membership; -using Umbraco.Cms.Core.Strings; - -namespace Umbraco.Tests.TestHelpers.Entities -{ - public class MockedUserGroup - { - public static IShortStringHelper ShortStringHelper { get; } = - new DefaultShortStringHelper(new DefaultShortStringHelperConfig()); - - public static UserGroup CreateUserGroup(string suffix = "", string[] permissions = null, string[] allowedSections = null) - { - var group = new UserGroup(ShortStringHelper) - { - Alias = "testUserGroup" + suffix, - Name = "TestUserGroup" + suffix, - Permissions = permissions ?? new[] { "A", "B", "C" } - }; - - if (allowedSections == null) - { - group.AddAllowedSection("content"); - group.AddAllowedSection("media"); - } - else - { - foreach (var allowedSection in allowedSections) - { - group.AddAllowedSection(allowedSection); - } - } - - return group; - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs b/tests/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs deleted file mode 100644 index bcbbdab59849..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/FakeHttpContextFactory.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Specialized; -using System.Security; -using System.Security.Principal; -using System.Web; -using System.Web.Routing; -using Moq; - -namespace Umbraco.Tests.TestHelpers -{ - /// - /// Creates a mock http context with supporting other contexts to test against - /// - public class FakeHttpContextFactory - { - - [SecuritySafeCritical] - public FakeHttpContextFactory(Uri fullUrl) - { - CreateContext(fullUrl); - } - - [SecuritySafeCritical] - public FakeHttpContextFactory(string path) - { - if (path.StartsWith("http://") || path.StartsWith("https://")) - CreateContext(new Uri(path)); - else - CreateContext(new Uri("http://mysite" + VirtualPathUtility.ToAbsolute(path, "/"))); - } - - [SecuritySafeCritical] - public FakeHttpContextFactory(string path, RouteData routeData) - { - if (path.StartsWith("http://") || path.StartsWith("https://")) - CreateContext(new Uri(path), routeData); - else - CreateContext(new Uri("http://mysite" + VirtualPathUtility.ToAbsolute(path, "/")), routeData); - } - - public HttpContextBase HttpContext { get; private set; } - public RequestContext RequestContext { get; private set; } - - /// - /// Mocks the http context to test against - /// - /// - /// - /// - private void CreateContext(Uri fullUrl, RouteData routeData = null) - { - //Request context - - var requestContextMock = new Mock(); - - RequestContext = requestContextMock.Object; - - //Cookie collection - var cookieCollection = new HttpCookieCollection(); - cookieCollection.Add(new HttpCookie("UMB_UCONTEXT", "FBA996E7-D6BE-489B-B199-2B0F3D2DD826")); - - //Request - var requestMock = new Mock(); - requestMock.Setup(x => x.AppRelativeCurrentExecutionFilePath).Returns("~" + fullUrl.AbsolutePath); - requestMock.Setup(x => x.PathInfo).Returns(string.Empty); - requestMock.Setup(x => x.RawUrl).Returns(VirtualPathUtility.ToAbsolute("~" + fullUrl.AbsolutePath, "/")); - requestMock.Setup(x => x.RequestContext).Returns(RequestContext); - requestMock.Setup(x => x.Url).Returns(fullUrl); - requestMock.Setup(x => x.ApplicationPath).Returns("/"); - requestMock.Setup(x => x.Cookies).Returns(cookieCollection); - requestMock.Setup(x => x.ServerVariables).Returns(new NameValueCollection()); - var queryStrings = HttpUtility.ParseQueryString(fullUrl.Query); - requestMock.Setup(x => x.QueryString).Returns(queryStrings); - requestMock.Setup(x => x.Form).Returns(new NameValueCollection()); - - //Cache - var cacheMock = new Mock(); - - //Response - //var response = new FakeHttpResponse(); - var responseMock = new Mock(); - responseMock.Setup(x => x.ApplyAppPathModifier(It.IsAny())).Returns((string s) => s); - responseMock.Setup(x => x.Cache).Returns(cacheMock.Object); - - //Server - - var serverMock = new Mock(); - serverMock.Setup(x => x.MapPath(It.IsAny())).Returns(Environment.CurrentDirectory); - - //User - var user = new Mock().Object; - - //HTTP Context - - var httpContextMock = new Mock(); - httpContextMock.Setup(x => x.Cache).Returns(HttpRuntime.Cache); - //note: foreach on Items should return DictionaryEntries! - //httpContextMock.Setup(x => x.Items).Returns(new Dictionary()); - httpContextMock.Setup(x => x.Items).Returns(new Hashtable()); - httpContextMock.Setup(x => x.Request).Returns(requestMock.Object); - httpContextMock.Setup(x => x.Server).Returns(serverMock.Object); - httpContextMock.Setup(x => x.Response).Returns(responseMock.Object); - httpContextMock.Setup(x => x.User).Returns(user); - - HttpContext = httpContextMock.Object; - - requestContextMock.Setup(x => x.HttpContext).Returns(httpContextMock.Object); - - - if (routeData is null) - { - routeData = new RouteData(); - } - - requestContextMock.Setup(x => x.RouteData).Returns(routeData); - } - - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs b/tests/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs deleted file mode 100644 index cf8820828669..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Stubs/TestControllerFactory.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Web.Mvc; -using System.Web.Routing; -using System.Web.SessionState; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Web; -using Umbraco.Extensions; -using Current = Umbraco.Web.Composing.Current; - -namespace Umbraco.Tests.TestHelpers.Stubs -{ - /// - /// Used in place of the UmbracoControllerFactory which relies on BuildManager which throws exceptions in a unit test context - /// - internal class TestControllerFactory : IControllerFactory - { - private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly ILogger _logger; - private readonly Func _factory; - - public TestControllerFactory(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger) - { - _umbracoContextAccessor = umbracoContextAccessor; - _logger = logger; - } - - public TestControllerFactory(IUmbracoContextAccessor umbracoContextAccessor, ILogger logger, Func factory) - { - _umbracoContextAccessor = umbracoContextAccessor; - _logger = logger; - _factory = factory; - } - - public IController CreateController(RequestContext requestContext, string controllerName) - { - if (_factory != null) return _factory(requestContext); - - var typeFinder = TestHelper.GetTypeFinder(); - var types = typeFinder.FindClassesOfType(new[] { Assembly.GetExecutingAssembly() }); - - var controllerTypes = types.Where(x => x.Name.Equals(controllerName + "Controller", StringComparison.InvariantCultureIgnoreCase)); - var t = controllerTypes.SingleOrDefault(); - - if (t == null) - return null; - - var possibleParams = new object[] - { - _umbracoContextAccessor, _logger - }; - var ctors = t.GetConstructors(); - foreach (var ctor in ctors.OrderByDescending(x => x.GetParameters().Length)) - { - var args = new List(); - var allParams = ctor.GetParameters().ToArray(); - foreach (var parameter in allParams) - { - var found = possibleParams.SingleOrDefault(x => x.GetType() == parameter.ParameterType) - ?? Current.Factory.GetRequiredService(parameter.ParameterType); - if (found != null) args.Add(found); - } - if (args.Count == allParams.Length) - { - return Activator.CreateInstance(t, args.ToArray()) as IController; - } - } - return null; - } - - public SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName) - { - return SessionStateBehavior.Disabled; - } - - public void ReleaseController(IController controller) - { - controller.DisposeIfDisposable(); - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs b/tests/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs deleted file mode 100644 index 7bb3b90d8156..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Stubs/TestExamineManager.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; -using Examine; - -namespace Umbraco.Tests.TestHelpers.Stubs -{ - internal class TestExamineManager : IExamineManager - { - private readonly ConcurrentDictionary _indexers = new ConcurrentDictionary(); - private readonly ConcurrentDictionary _searchers = new ConcurrentDictionary(); - - public IIndex AddIndex(IIndex indexer) - { - _indexers.TryAdd(indexer.Name, indexer); - return indexer; - } - - public ISearcher AddSearcher(ISearcher searcher) - { - _searchers.TryAdd(searcher.Name, searcher); - return searcher; - } - - public void Dispose() - { - //noop - } - - public bool TryGetIndex(string indexName, out IIndex index) - { - return _indexers.TryGetValue(indexName, out index); - } - - public bool TryGetSearcher(string searcherName, out ISearcher searcher) - { - return _searchers.TryGetValue(searcherName, out searcher); - } - - public IEnumerable Indexes => _indexers.Values; - - public IEnumerable RegisteredSearchers => _searchers.Values; - - public IReadOnlyDictionary IndexProviders => _indexers; - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Stubs/TestLastChanceFinder.cs b/tests/Umbraco.Tests/TestHelpers/Stubs/TestLastChanceFinder.cs deleted file mode 100644 index 89798b877132..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Stubs/TestLastChanceFinder.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Umbraco.Cms.Core.Routing; - -namespace Umbraco.Tests.TestHelpers.Stubs -{ - internal class TestLastChanceFinder : IContentLastChanceFinder - { - public bool TryFindContent(IPublishedRequestBuilder frequest) => false; - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/Stubs/TestUserPasswordConfig.cs b/tests/Umbraco.Tests/TestHelpers/Stubs/TestUserPasswordConfig.cs deleted file mode 100644 index 065305c63a76..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/Stubs/TestUserPasswordConfig.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Umbraco.Cms.Core.Configuration; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.TestHelpers.Stubs -{ - internal class TestUserPasswordConfig : IUserPasswordConfiguration - { - public int RequiredLength => 12; - - public bool RequireNonLetterOrDigit => false; - - public bool RequireDigit => false; - - public bool RequireLowercase => false; - - public bool RequireUppercase => false; - - public bool UseLegacyEncoding => false; - - public string HashAlgorithmType => Constants.Security.AspNetUmbraco8PasswordHashAlgorithmName; - - public int MaxFailedAccessAttemptsBeforeLockout => 5; - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/TestHelper.cs b/tests/Umbraco.Tests/TestHelpers/TestHelper.cs deleted file mode 100644 index b8960c6b64e1..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/TestHelper.cs +++ /dev/null @@ -1,346 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading; -using System.Web; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Diagnostics; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Mail; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.Entities; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Net; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Runtime; -using Umbraco.Cms.Core.Serialization; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Infrastructure; -using Umbraco.Cms.Infrastructure.Migrations.Install; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Persistence.SqlCe; -using Umbraco.Cms.Tests.Common; -using Umbraco.Extensions; -using Umbraco.Web; -using Umbraco.Web.Hosting; -using Constants = Umbraco.Cms.Core.Constants; -using File = System.IO.File; - -namespace Umbraco.Tests.TestHelpers -{ - /// - /// Common helper properties and methods useful to testing - /// - public static class TestHelper - { - private static readonly TestHelperInternal _testHelperInternal = new TestHelperInternal(); - private static IEmailSender _emailSender; - - private class TestHelperInternal : TestHelperBase - { - public TestHelperInternal() : base(typeof(TestHelperInternal).Assembly) - { - - } - - public override IDbProviderFactoryCreator DbProviderFactoryCreator { get; } = new UmbracoDbProviderFactoryCreator(); - public DatabaseSchemaCreatorFactory DatabaseSchemaCreatorFactory { get; } = new DatabaseSchemaCreatorFactory(Mock.Of>(), NullLoggerFactory.Instance, new UmbracoVersion(), Mock.Of()); - - public override IBulkSqlInsertProvider BulkSqlInsertProvider { get; } = new SqlCeBulkSqlInsertProvider(); - - public override IMarchal Marchal { get; } = new FrameworkMarchal(); - - public override IHostingEnvironment GetHostingEnvironment() - => new AspNetHostingEnvironment(Options.Create(new HostingSettings())); - - public override IApplicationShutdownRegistry GetHostingEnvironmentLifetime() - => new AspNetApplicationShutdownRegistry(); - - public override IIpResolver GetIpResolver() - => new AspNetIpResolver(); - } - - public static ITypeFinder GetTypeFinder() => _testHelperInternal.GetTypeFinder(); - - public static TypeLoader GetMockedTypeLoader() => _testHelperInternal.GetMockedTypeLoader(); - - /// - /// Gets the working directory of the test project. - /// - /// The assembly directory. - public static string WorkingDirectory => _testHelperInternal.WorkingDirectory; - - public static IShortStringHelper ShortStringHelper => _testHelperInternal.ShortStringHelper; - public static IJsonSerializer JsonSerializer => _testHelperInternal.JsonSerializer; - public static IVariationContextAccessor VariationContextAccessor => _testHelperInternal.VariationContextAccessor; - public static IDbProviderFactoryCreator DbProviderFactoryCreator => _testHelperInternal.DbProviderFactoryCreator; - public static DatabaseSchemaCreatorFactory DatabaseSchemaCreatorFactory => _testHelperInternal.DatabaseSchemaCreatorFactory; - public static IBulkSqlInsertProvider BulkSqlInsertProvider => _testHelperInternal.BulkSqlInsertProvider; - public static IMarchal Marchal => _testHelperInternal.Marchal; - public static CoreDebugSettings CoreDebugSettings => _testHelperInternal.CoreDebugSettings; - - - public static IIOHelper IOHelper => _testHelperInternal.IOHelper; - public static IMainDom MainDom => _testHelperInternal.MainDom; - public static UriUtility UriUtility => _testHelperInternal.UriUtility; - - public static IEmailSender EmailSender { get; } = new EmailSender(Options.Create(new GlobalSettings())); - - - /// - /// Some test files are copied to the /bin (/bin/debug) on build, this is a utility to return their physical path based on a virtual path name - /// - /// - /// - public static string MapPathForTestFiles(string relativePath) => _testHelperInternal.MapPathForTestFiles(relativePath); - - public static void InitializeContentDirectories() - { - CreateDirectories(new[] { Constants.SystemDirectories.MvcViews, new GlobalSettings().UmbracoMediaPath, Constants.SystemDirectories.AppPlugins }); - } - - public static void CleanContentDirectories() - { - CleanDirectories(new[] { Constants.SystemDirectories.MvcViews, new GlobalSettings().UmbracoMediaPath }); - } - - public static void CreateDirectories(string[] directories) - { - foreach (var directory in directories) - { - var directoryInfo = new DirectoryInfo(IOHelper.MapPath(directory)); - if (directoryInfo.Exists == false) - Directory.CreateDirectory(IOHelper.MapPath(directory)); - } - } - - public static void CleanDirectories(string[] directories) - { - var preserves = new Dictionary - { - { Constants.SystemDirectories.MvcViews, new[] {"dummy.txt"} } - }; - foreach (var directory in directories) - { - var directoryInfo = new DirectoryInfo(IOHelper.MapPath(directory)); - var preserve = preserves.ContainsKey(directory) ? preserves[directory] : null; - if (directoryInfo.Exists) - foreach (var x in directoryInfo.GetFiles().Where(x => preserve == null || preserve.Contains(x.Name) == false)) - x.Delete(); - } - } - - public static void CleanUmbracoSettingsConfig() - { - var currDir = new DirectoryInfo(WorkingDirectory); - - var umbracoSettingsFile = Path.Combine(currDir.Parent.Parent.FullName, "config", "umbracoSettings.config"); - if (File.Exists(umbracoSettingsFile)) - File.Delete(umbracoSettingsFile); - } - - // TODO: Move to Assertions or AssertHelper - // FIXME: obsolete the dateTimeFormat thing and replace with dateDelta - public static void AssertPropertyValuesAreEqual(object actual, object expected, string dateTimeFormat = null, Func sorter = null, string[] ignoreProperties = null) - { - const int dateDeltaMilliseconds = 500; // .5s - - var properties = expected.GetType().GetProperties(); - foreach (var property in properties) - { - // ignore properties that are attributed with EditorBrowsableState.Never - var att = property.GetCustomAttribute(false); - if (att != null && att.State == EditorBrowsableState.Never) - continue; - - // ignore explicitely ignored properties - if (ignoreProperties != null && ignoreProperties.Contains(property.Name)) - continue; - - var actualValue = property.GetValue(actual, null); - var expectedValue = property.GetValue(expected, null); - - AssertAreEqual(property, expectedValue, actualValue, sorter, dateDeltaMilliseconds); - } - } - - private static void AssertAreEqual(PropertyInfo property, object expected, object actual, Func sorter = null, int dateDeltaMilliseconds = 0) - { - if (!(expected is string) && expected is IEnumerable) - { - // sort property collection by alias, not by property ids - // on members, built-in properties don't have ids (always zero) - if (expected is PropertyCollection) - sorter = e => ((PropertyCollection) e).OrderBy(x => x.Alias); - - // compare lists - AssertListsAreEqual(property, (IEnumerable) actual, (IEnumerable) expected, sorter, dateDeltaMilliseconds); - } - else if (expected is DateTime expectedDateTime) - { - // compare date & time with delta - var actualDateTime = (DateTime) actual; - var delta = (actualDateTime - expectedDateTime).TotalMilliseconds; - Assert.IsTrue(Math.Abs(delta) <= dateDeltaMilliseconds, "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expected, actual); - } - else if (expected is Property expectedProperty) - { - // compare values - var actualProperty = (Property) actual; - var expectedPropertyValues = expectedProperty.Values.OrderBy(x => x.Culture).ThenBy(x => x.Segment).ToArray(); - var actualPropertyValues = actualProperty.Values.OrderBy(x => x.Culture).ThenBy(x => x.Segment).ToArray(); - if (expectedPropertyValues.Length != actualPropertyValues.Length) - Assert.Fail($"{property.DeclaringType.Name}.{property.Name}: Expected {expectedPropertyValues.Length} but got {actualPropertyValues.Length}."); - for (var i = 0; i < expectedPropertyValues.Length; i++) - { - Assert.AreEqual(expectedPropertyValues[i].EditedValue, actualPropertyValues[i].EditedValue, $"{property.DeclaringType.Name}.{property.Name}: Expected draft value \"{expectedPropertyValues[i].EditedValue}\" but got \"{actualPropertyValues[i].EditedValue}\"."); - Assert.AreEqual(expectedPropertyValues[i].PublishedValue, actualPropertyValues[i].PublishedValue, $"{property.DeclaringType.Name}.{property.Name}: Expected published value \"{expectedPropertyValues[i].EditedValue}\" but got \"{actualPropertyValues[i].EditedValue}\"."); - } - } - else if (expected is IDataEditor expectedEditor) - { - Assert.IsInstanceOf(actual); - var actualEditor = (IDataEditor) actual; - Assert.AreEqual(expectedEditor.Alias, actualEditor.Alias); - // what else shall we test? - } - else - { - // directly compare values - Assert.AreEqual(expected, actual, "Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, - expected?.ToString() ?? "", actual?.ToString() ?? ""); - } - } - - private static void AssertListsAreEqual(PropertyInfo property, IEnumerable expected, IEnumerable actual, Func sorter = null, int dateDeltaMilliseconds = 0) - { - - - if (sorter == null) - { - // this is pretty hackerific but saves us some code to write - sorter = enumerable => - { - // semi-generic way of ensuring any collection of IEntity are sorted by Ids for comparison - var entities = enumerable.OfType().ToList(); - return entities.Count > 0 ? (IEnumerable) entities.OrderBy(x => x.Id) : entities; - }; - } - - var expectedListEx = sorter(expected).Cast().ToList(); - var actualListEx = sorter(actual).Cast().ToList(); - - if (actualListEx.Count != expectedListEx.Count) - Assert.Fail("Collection {0}.{1} does not match. Expected IEnumerable containing {2} elements but was IEnumerable containing {3} elements", property.PropertyType.Name, property.Name, expectedListEx.Count, actualListEx.Count); - - for (var i = 0; i < actualListEx.Count; i++) - AssertAreEqual(property, expectedListEx[i], actualListEx[i], sorter, dateDeltaMilliseconds); - } - - public static void DeleteDirectory(string path) - { - Try(() => - { - if (Directory.Exists(path) == false) return; - foreach (var file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)) - File.Delete(file); - }); - - Try(() => - { - if (Directory.Exists(path) == false) return; - Directory.Delete(path, true); - }); - } - - public static void TryAssert(Action action, int maxTries = 5, int waitMilliseconds = 200) - { - Try(action, maxTries, waitMilliseconds); - } - - public static void Try(Action action, int maxTries = 5, int waitMilliseconds = 200) - { - Try(action, maxTries, waitMilliseconds); - } - - public static void Try(Action action, int maxTries = 5, int waitMilliseconds = 200) - where T : Exception - { - var tries = 0; - while (true) - { - try - { - action(); - break; - } - catch (T) - { - if (tries++ > maxTries) - throw; - Thread.Sleep(waitMilliseconds); - } - } - } - - public static IUmbracoVersion GetUmbracoVersion() => _testHelperInternal.GetUmbracoVersion(); - - public static IServiceCollection GetRegister() => _testHelperInternal.GetRegister().AddLazySupport(); - - public static IHostingEnvironment GetHostingEnvironment() => _testHelperInternal.GetHostingEnvironment(); - - public static ILoggingConfiguration GetLoggingConfiguration(IHostingEnvironment hostingEnv) => _testHelperInternal.GetLoggingConfiguration(hostingEnv); - - public static IApplicationShutdownRegistry GetHostingEnvironmentLifetime() => _testHelperInternal.GetHostingEnvironmentLifetime(); - - public static IIpResolver GetIpResolver() => _testHelperInternal.GetIpResolver(); - - public static IRequestCache GetRequestCache() => _testHelperInternal.GetRequestCache(); - - public static IHttpContextAccessor GetHttpContextAccessor(HttpContextBase httpContextBase = null) - { - if (httpContextBase is null) - { - var httpContextMock = new Mock(); - - httpContextMock.Setup(x => x.DisposeOnPipelineCompleted(It.IsAny())) - .Returns(Mock.Of()); - - httpContextBase = httpContextMock.Object; - } - - var mock = new Mock(); - - mock.Setup(x => x.HttpContext).Returns(httpContextBase); - - return mock.Object; - } - - public static IPublishedUrlProvider GetPublishedUrlProvider() => _testHelperInternal.GetPublishedUrlProvider(); - - public static IServiceProvider CreateServiceProvider(IUmbracoBuilder builder) - { - builder.Build(); - return builder.Services.BuildServiceProvider(); - } - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs b/tests/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs deleted file mode 100644 index 1f32e739162e..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/TestObjects-Mocks.cs +++ /dev/null @@ -1,327 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Data.Common; -using System.Linq; -using System.Linq.Expressions; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Moq; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Persistence.SqlCe; -using Umbraco.Cms.Tests.Common; -using Umbraco.Cms.Tests.Common.TestHelpers; -using Umbraco.Extensions; -using Umbraco.Web; - -namespace Umbraco.Tests.TestHelpers -{ - /// - /// Provides objects for tests. - /// - internal partial class TestObjects - { - /// - /// Gets a mocked IUmbracoDatabaseFactory. - /// - /// An IUmbracoDatabaseFactory. - /// A value indicating whether the factory is configured. - /// A value indicating whether the factory can connect to the database. - /// This is just a void factory that has no actual database. - public IUmbracoDatabaseFactory GetDatabaseFactoryMock(bool configured = true, bool canConnect = true) - { - var sqlSyntax = new SqlCeSyntaxProvider(Options.Create(new GlobalSettings())); - var sqlContext = Mock.Of(); - Mock.Get(sqlContext).Setup(x => x.SqlSyntax).Returns(sqlSyntax); - - var databaseFactoryMock = new Mock(); - databaseFactoryMock.Setup(x => x.Configured).Returns(configured); - databaseFactoryMock.Setup(x => x.CanConnect).Returns(canConnect); - databaseFactoryMock.Setup(x => x.SqlContext).Returns(sqlContext); - - // can create a database - but don't try to use it! - if (configured && canConnect) - databaseFactoryMock.Setup(x => x.CreateDatabase()).Returns(GetUmbracoSqlCeDatabase(Mock.Of>())); - - return databaseFactoryMock.Object; - } - - /// - /// Gets a mocked service context built with mocked services. - /// - /// A ServiceContext. - public ServiceContext GetServiceContextMock(IServiceProvider container = null) - { - // FIXME: else some tests break - figure it out - container = null; - - return ServiceContext.CreatePartial( - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container), - MockService(container)); - } - - private T MockService(IServiceProvider container = null) - where T : class - { - return container?.GetService() ?? new Mock().Object; - } - - /// - /// Gets an opened database connection that can begin a transaction. - /// - /// A DbConnection. - /// This is because NPoco wants a DbConnection, NOT an IDbConnection, - /// and DbConnection is hard to mock so we create our own class here. - public DbConnection GetDbConnection() - { - return new MockDbConnection(); - } - - /// - /// Gets an Umbraco context. - /// - /// An Umbraco context. - /// This should be the minimum Umbraco context. - public IUmbracoContext GetUmbracoContextMock(IUmbracoContextAccessor accessor = null) - { - - var publishedSnapshotMock = new Mock(); - publishedSnapshotMock.Setup(x => x.Members).Returns(Mock.Of()); - var publishedSnapshot = publishedSnapshotMock.Object; - var publishedSnapshotServiceMock = new Mock(); - publishedSnapshotServiceMock.Setup(x => x.CreatePublishedSnapshot(It.IsAny())).Returns(publishedSnapshot); - var publishedSnapshotService = publishedSnapshotServiceMock.Object; - - var globalSettings = GetGlobalSettings(); - - if (accessor == null) accessor = new TestUmbracoContextAccessor(); - - var httpContextAccessor = TestHelper.GetHttpContextAccessor(); - - var umbracoContextFactory = new UmbracoContextFactory( - accessor, - publishedSnapshotService, - new TestVariationContextAccessor(), - new TestDefaultCultureAccessor(), - globalSettings, - Mock.Of(), - TestHelper.GetHostingEnvironment(), - TestHelper.UriUtility, - httpContextAccessor, - new AspNetCookieManager(httpContextAccessor)); - - return umbracoContextFactory.EnsureUmbracoContext().UmbracoContext; - } - - public GlobalSettings GetGlobalSettings() - { - return new GlobalSettings(); - } - public FileSystems GetFileSystemsMock() - { - var fileSystems = FileSystemsCreator.CreateTestFileSystems( - NullLoggerFactory.Instance, - Mock.Of(), - Mock.Of>(), - Mock.Of(), - Mock.Of(), - Mock.Of(), - Mock.Of(), - Mock.Of(), - Mock.Of() - ); - - return fileSystems; - } - - #region Inner classes - - private class MockDbConnection : DbConnection - { - protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) - { - return Mock.Of(); // enough here - } - - public override void Close() - { - throw new NotImplementedException(); - } - - public override void ChangeDatabase(string databaseName) - { - throw new NotImplementedException(); - } - - public override void Open() - { - throw new NotImplementedException(); - } - - public override string ConnectionString { get; set; } - - protected override DbCommand CreateDbCommand() - { - throw new NotImplementedException(); - } - - public override string Database { get; } - public override string DataSource { get; } - public override string ServerVersion { get; } - public override ConnectionState State => ConnectionState.Open; // else NPoco reopens - } - - public class TestDataTypeService : IDataTypeService - { - public TestDataTypeService() - { - DataTypes = new Dictionary(); - } - - public TestDataTypeService(params IDataType[] dataTypes) - { - DataTypes = dataTypes.ToDictionary(x => x.Id, x => x); - } - - public TestDataTypeService(IEnumerable dataTypes) - { - DataTypes = dataTypes.ToDictionary(x => x.Id, x => x); - } - - public Dictionary DataTypes { get; } - - public Attempt> CreateContainer(int parentId, string name, int userId = -1) - { - throw new NotImplementedException(); - } - - public Attempt SaveContainer(EntityContainer container, int userId = -1) - { - throw new NotImplementedException(); - } - - public EntityContainer GetContainer(int containerId) - { - throw new NotImplementedException(); - } - - public EntityContainer GetContainer(Guid containerId) - { - throw new NotImplementedException(); - } - - public IEnumerable GetContainers(string folderName, int level) - { - throw new NotImplementedException(); - } - - public IEnumerable GetContainers(IDataType dataType) - { - throw new NotImplementedException(); - } - - public IEnumerable GetContainers(int[] containerIds) - { - throw new NotImplementedException(); - } - - public Attempt DeleteContainer(int containerId, int userId = -1) - { - throw new NotImplementedException(); - } - - public Attempt> RenameContainer(int id, string name, int userId = -1) - { - throw new NotImplementedException(); - } - - public IDataType GetDataType(string name) - { - throw new NotImplementedException(); - } - - public IDataType GetDataType(int id) - { - DataTypes.TryGetValue(id, out var dataType); - return dataType; - } - - public IDataType GetDataType(Guid id) - { - throw new NotImplementedException(); - } - - public IEnumerable GetAll(params int[] ids) - { - if (ids.Length == 0) return DataTypes.Values; - return ids.Select(x => DataTypes.TryGetValue(x, out var dataType) ? dataType : null).WhereNotNull(); - } - - public void Save(IDataType dataType, int userId = -1) - { - throw new NotImplementedException(); - } - - public void Save(IEnumerable dataTypeDefinitions, int userId = -1) - { - throw new NotImplementedException(); - } - - public void Save(IEnumerable dataTypeDefinitions, int userId, bool raiseEvents) - { - throw new NotImplementedException(); - } - - public void Delete(IDataType dataType, int userId = -1) - { - throw new NotImplementedException(); - } - - public IEnumerable GetByEditorAlias(string propertyEditorAlias) - { - throw new NotImplementedException(); - } - - public Attempt> Move(IDataType toMove, int parentId) - { - throw new NotImplementedException(); - } - - public IReadOnlyDictionary> GetReferences(int id) - { - throw new NotImplementedException(); - } - } - - #endregion - - - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/TestObjects.cs b/tests/Umbraco.Tests/TestHelpers/TestObjects.cs deleted file mode 100644 index 6f7fcfe2ddec..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/TestObjects.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Moq; -using NPoco; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Infrastructure.Migrations.Install; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.Mappers; -using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; -using Umbraco.Cms.Persistence.SqlCe; -using Umbraco.Web.Composing; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.TestHelpers -{ - /// - /// Provides objects for tests. - /// - internal partial class TestObjects - { - - public TestObjects() - { - } - - /// - /// Gets an UmbracoDatabase. - /// - /// A logger. - /// An UmbracoDatabase. - /// This is just a void database that has no actual database but pretends to have an open connection - /// that can begin a transaction. - public UmbracoDatabase GetUmbracoSqlCeDatabase(ILogger logger) - { - var syntax = new SqlCeSyntaxProvider(Options.Create(new GlobalSettings())); - var connection = GetDbConnection(); - var sqlContext = new SqlContext(syntax, DatabaseType.SQLCe, Mock.Of()); - return new UmbracoDatabase(connection, sqlContext, logger, TestHelper.BulkSqlInsertProvider); - } - - /// - /// Gets an UmbracoDatabase. - /// - /// A logger. - /// An UmbracoDatabase. - /// This is just a void database that has no actual database but pretends to have an open connection - /// that can begin a transaction. - public UmbracoDatabase GetUmbracoSqlServerDatabase(ILogger logger) - { - var syntax = new SqlServerSyntaxProvider(Options.Create(new GlobalSettings())); // do NOT try to get the server's version! - var connection = GetDbConnection(); - var sqlContext = new SqlContext(syntax, DatabaseType.SqlServer2008, Mock.Of()); - return new UmbracoDatabase(connection, sqlContext, logger, TestHelper.BulkSqlInsertProvider); - } - - public IScopeProvider GetScopeProvider(ILoggerFactory loggerFactory, FileSystems fileSystems = null, IUmbracoDatabaseFactory databaseFactory = null) - { - var globalSettings = Options.Create(new GlobalSettings()); - var connectionString = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName].ConnectionString; - var connectionStrings = Options.Create(new ConnectionStrings { UmbracoConnectionString = new ConfigConnectionString(Constants.System.UmbracoConnectionName, connectionString) }); - var coreDebugSettings = new CoreDebugSettings(); - - if (databaseFactory == null) - { - // var mappersBuilder = new MapperCollectionBuilder(Current.Container); // FIXME: - // mappersBuilder.AddCore(); - // var mappers = mappersBuilder.CreateCollection(); - var mappers = Current.Factory.GetRequiredService(); - databaseFactory = new UmbracoDatabaseFactory( - loggerFactory.CreateLogger(), - loggerFactory, - globalSettings, - connectionStrings, - new Lazy(() => mappers), - TestHelper.DbProviderFactoryCreator, - new DatabaseSchemaCreatorFactory(Mock.Of>(),loggerFactory, new UmbracoVersion(), Mock.Of())); - } - - fileSystems ??= new FileSystems(loggerFactory, TestHelper.IOHelper, globalSettings, TestHelper.GetHostingEnvironment()); - var coreDebug = TestHelper.CoreDebugSettings; - var mediaFileManager = Mock.Of(); - var eventAggregator = Mock.Of(); - return new ScopeProvider(databaseFactory, fileSystems, Options.Create(coreDebugSettings), mediaFileManager, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); - } - - } -} diff --git a/tests/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/tests/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs deleted file mode 100644 index 9c0651037067..000000000000 --- a/tests/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ /dev/null @@ -1,429 +0,0 @@ -using System; -using System.Configuration; -using System.Data.SqlServerCe; -using System.IO; -using System.Threading; -using System.Web.Routing; -using System.Xml; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Moq; -using NUnit.Framework; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Persistence.Repositories; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Migrations.Install; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.Mappers; -using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; -using Umbraco.Cms.Persistence.SqlCe; -using Umbraco.Cms.Tests.Common; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.LegacyXmlPublishedCache; -using Umbraco.Tests.Testing; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web.WebApi; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Tests.TestHelpers -{ - /// - /// Provides a base class for all Umbraco tests that require a database. - /// - /// - /// Can provide a SqlCE database populated with the Umbraco schema. The database should - /// be accessed through the UmbracoDatabaseFactory. - /// Provides an Umbraco context and Xml content. - /// fixme what else? - /// - [UmbracoTest(WithApplication = true)] - public abstract class TestWithDatabaseBase : UmbracoTestBase - { - private string _databasePath; - private static byte[] _databaseBytes; - - protected PublishedContentTypeCache ContentTypesCache { get; private set; } - - protected override ISqlSyntaxProvider SqlSyntax => GetSyntaxProvider(); - protected IVariationContextAccessor VariationContextAccessor => new TestVariationContextAccessor(); - - internal ScopeProvider ScopeProvider => Current.ScopeProvider as ScopeProvider; - internal IUmbracoDatabaseFactory UmbracoDatabaseFactory => Factory.GetRequiredService(); - internal IDataValueEditorFactory DataValueEditorFactory => Factory.GetRequiredService(); - - protected ISqlContext SqlContext => Factory.GetRequiredService(); - - public override void SetUp() - { - // Ensure the data directory is set before continuing - var path = TestHelper.WorkingDirectory; - AppDomain.CurrentDomain.SetData("DataDirectory", path); - - base.SetUp(); - } - - protected override void Compose() - { - base.Compose(); - - Builder.Services.AddTransient(); - Builder.Services.AddTransient(factory => PublishedSnapshotService); - Builder.Services.AddTransient(factory => DefaultCultureAccessor); - - Builder.WithCollectionBuilder() - .Clear() - .Add(() => Builder.TypeLoader.GetDataEditors()); - - Builder.WithCollectionBuilder() - .Add(Builder.TypeLoader.GetUmbracoApiControllers()); - - Builder.Services.AddUnique(f => - { - if (Options.Database == UmbracoTestOptions.Database.None) - return TestObjects.GetDatabaseFactoryMock(); - - var lazyMappers = new Lazy(f.GetRequiredService); - var factory = new UmbracoDatabaseFactory(f.GetRequiredService>(), f.GetRequiredService(), GetDbConnectionString(), GetDbProviderName(), lazyMappers, TestHelper.DbProviderFactoryCreator, f.GetRequiredService()); - return factory; - }); - } - - [OneTimeTearDown] - public void FixtureTearDown() - { - RemoveDatabaseFile(); - } - - public override void TearDown() - { - var profilingLogger = Factory.GetService(); - var timer = profilingLogger?.TraceDuration("teardown"); // FIXME: move that one up - try - { - // FIXME: should we first kill all scopes? - if (Options.Database == UmbracoTestOptions.Database.NewSchemaPerTest) - RemoveDatabaseFile(); - - AppDomain.CurrentDomain.SetData("DataDirectory", null); - - // make sure we dispose of the service to unbind events - PublishedSnapshotService?.Dispose(); - PublishedSnapshotService = null; - } - finally - { - timer?.Dispose(); - } - - base.TearDown(); - } - - private void CreateAndInitializeDatabase() - { - using (ProfilingLogger.TraceDuration("Create database.")) - { - CreateSqlCeDatabase(); // TODO: faster! - } - - using (ProfilingLogger.TraceDuration("Initialize database.")) - { - InitializeDatabase(); // TODO: faster! - } - } - - protected virtual ISqlSyntaxProvider GetSyntaxProvider() - { - return new SqlCeSyntaxProvider(Microsoft.Extensions.Options.Options.Create(new GlobalSettings())); - } - - protected virtual string GetDbProviderName() - { - return Constants.DbProviderNames.SqlCe; - } - - protected virtual string GetDbConnectionString() - { - return @"DataSource=|DataDirectory|UmbracoNPocoTests.sdf;Flush Interval=1;"; - } - - - - /// - /// Creates the SqlCe database if required - /// - protected virtual void CreateSqlCeDatabase() - { - if (Options.Database == UmbracoTestOptions.Database.None) - return; - - var path = TestHelper.WorkingDirectory; - - //Get the connectionstring settings from config - var settings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName]; - ConfigurationManager.AppSettings.Set( - Constants.System.UmbracoConnectionName, - GetDbConnectionString()); - - _databasePath = string.Concat(path, "\\UmbracoNPocoTests.sdf"); - - //create a new database file if - // - is the first test in the session - // - the database file doesn't exist - // - NewDbFileAndSchemaPerTest - // - _isFirstTestInFixture + DbInitBehavior.NewDbFileAndSchemaPerFixture - - //if this is the first test in the session, always ensure a new db file is created - if (FirstTestInSession - || File.Exists(_databasePath) == false - || Options.Database == UmbracoTestOptions.Database.NewSchemaPerTest - || Options.Database == UmbracoTestOptions.Database.NewEmptyPerTest - || (FirstTestInFixture && Options.Database == UmbracoTestOptions.Database.NewSchemaPerFixture)) - { - using (ProfilingLogger.TraceDuration("Remove database file")) - { - RemoveDatabaseFile(null, ex => - { - //if this doesn't work we have to make sure everything is reset! otherwise - // well run into issues because we've already set some things up - TearDown(); - throw ex; - }); - } - - //Create the Sql CE database - using (ProfilingLogger.TraceDuration("Create database file")) - { - if (Options.Database != UmbracoTestOptions.Database.NewEmptyPerTest && _databaseBytes != null) - { - File.WriteAllBytes(_databasePath, _databaseBytes); - } - else - { - using (var engine = new SqlCeEngine(settings.ConnectionString)) - { - engine.CreateDatabase(); - } - } - } - - } - } - - protected IDefaultCultureAccessor DefaultCultureAccessor { get; set; } - - protected IPublishedSnapshotService PublishedSnapshotService { get; set; } - - protected override void Initialize() // FIXME: should NOT be here! - { - base.Initialize(); - - DefaultCultureAccessor = new TestDefaultCultureAccessor(); - - CreateAndInitializeDatabase(); - - // ensure we have a PublishedSnapshotService - if (PublishedSnapshotService == null) - { - PublishedSnapshotService = CreatePublishedSnapshotService(); - } - } - - protected virtual IPublishedSnapshotService CreatePublishedSnapshotService(GlobalSettings globalSettings = null) - { - var cache = NoAppCache.Instance; - - ContentTypesCache ??= new PublishedContentTypeCache( - Factory.GetRequiredService(), - Factory.GetRequiredService(), - Factory.GetRequiredService(), - Factory.GetRequiredService(), - Factory.GetRequiredService>()); - - // testing=true so XmlStore will not use the file nor the database - - var publishedSnapshotAccessor = new UmbracoContextPublishedSnapshotAccessor(Umbraco.Web.Composing.Current.UmbracoContextAccessor); - var variationContextAccessor = new TestVariationContextAccessor(); - var service = new XmlPublishedSnapshotService( - ServiceContext, - Factory.GetRequiredService(), - ScopeProvider, - cache, publishedSnapshotAccessor, variationContextAccessor, - Factory.GetRequiredService(), - Factory.GetRequiredService(), Factory.GetRequiredService(), Factory.GetRequiredService(), - DefaultCultureAccessor, - Factory.GetRequiredService(), - globalSettings ?? TestObjects.GetGlobalSettings(), - HostingEnvironment, - HostingLifetime, - ShortStringHelper, - Factory.GetRequiredService(), - ContentTypesCache, - null, true, Options.PublishedRepositoryEvents); - - // initialize PublishedCacheService content with an Xml source - service.XmlStore.GetXmlDocument = () => - { - var doc = new XmlDocument(); - doc.LoadXml(GetXmlContent(0)); - return doc; - }; - - return service; - } - - /// - /// Creates the tables and data for the database - /// - protected virtual void InitializeDatabase() - { - if (Options.Database == UmbracoTestOptions.Database.None || Options.Database == UmbracoTestOptions.Database.NewEmptyPerTest) - return; - - //create the schema and load default data if: - // - is the first test in the session - // - NewDbFileAndSchemaPerTest - // - _isFirstTestInFixture + DbInitBehavior.NewDbFileAndSchemaPerFixture - - if (_databaseBytes == null && - (FirstTestInSession - || Options.Database == UmbracoTestOptions.Database.NewSchemaPerTest - || FirstTestInFixture && Options.Database == UmbracoTestOptions.Database.NewSchemaPerFixture)) - { - using (var scope = ScopeProvider.CreateScope()) - { - var schemaHelper = new DatabaseSchemaCreator(scope.Database, LoggerFactory.CreateLogger(), LoggerFactory, UmbracoVersion, Mock.Of()); - //Create the umbraco database and its base data - schemaHelper.InitializeDatabaseSchema(); - - //Special case, we need to create the xml cache tables manually since they are not part of the default - //setup. - //TODO: Remove this when we update all tests to use nucache - schemaHelper.CreateTable(); - schemaHelper.CreateTable(); - - scope.Complete(); - } - - _databaseBytes = File.ReadAllBytes(_databasePath); - } - } - - // FIXME: is this needed? - private void CloseDbConnections(IUmbracoDatabase database) - { - //Ensure that any database connections from a previous test is disposed. - //This is really just double safety as its also done in the TearDown. - database?.Dispose(); - //SqlCeContextGuardian.CloseBackgroundConnection(); - } - - private void RemoveDatabaseFile(IUmbracoDatabase database, Action onFail = null) - { - if (database != null) CloseDbConnections(database); - RemoveDatabaseFile(onFail); - } - - private void RemoveDatabaseFile(Action onFail = null) - { - var path = TestHelper.WorkingDirectory; - try - { - var filePath = string.Concat(path, "\\UmbracoNPocoTests.sdf"); - if (File.Exists(filePath)) - File.Delete(filePath); - } - catch (Exception ex) - { - LoggerFactory.CreateLogger().LogError(ex, "Could not remove the old database file"); - - // swallow this exception - that's because a sub class might require further teardown logic - onFail?.Invoke(ex); - } - } - - protected IUmbracoContextAccessor GetUmbracoContextAccessor(IUmbracoContext ctx) => new TestUmbracoContextAccessor(ctx); - - protected IUmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, GlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null) - { - // ensure we have a PublishedCachesService - var service = snapshotService ?? PublishedSnapshotService as XmlPublishedSnapshotService; - if (service == null) - throw new Exception("Not a proper XmlPublishedCache.PublishedCachesService."); - - if (service is XmlPublishedSnapshotService) - { - // re-initialize PublishedCacheService content with an Xml source with proper template id - ((XmlPublishedSnapshotService)service).XmlStore.GetXmlDocument = () => - { - var doc = new XmlDocument(); - doc.LoadXml(GetXmlContent(templateId)); - return doc; - }; - } - - var httpContext = GetHttpContextFactory(url, routeData).HttpContext; - var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext); - var umbracoContext = new UmbracoContext( - httpContextAccessor, - service, - Mock.Of(), - globalSettings ?? new GlobalSettings(), - HostingEnvironment, - new TestVariationContextAccessor(), - UriUtility, - new AspNetCookieManager(httpContextAccessor)); - - if (setSingleton) - Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext; - - return umbracoContext; - } - - protected virtual string GetXmlContent(int templateId) - { - return @" - - - - -]> - - - - - 1 - - This is some content]]> - - - - - - - - - - - - - - - - -"; - } - } -} diff --git a/tests/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs b/tests/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs deleted file mode 100644 index e34ff7bb458f..000000000000 --- a/tests/Umbraco.Tests/Testing/Objects/Accessors/NoHttpContextAccessor.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Web; -using Umbraco.Web; - -namespace Umbraco.Tests.Testing.Objects.Accessors -{ - public class NoHttpContextAccessor : IHttpContextAccessor - { - public HttpContextBase HttpContext { get; set; } = null; - } -} diff --git a/tests/Umbraco.Tests/Testing/Objects/TestDataSource.cs b/tests/Umbraco.Tests/Testing/Objects/TestDataSource.cs deleted file mode 100644 index 97419ebe269e..000000000000 --- a/tests/Umbraco.Tests/Testing/Objects/TestDataSource.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Infrastructure.PublishedCache; -using Umbraco.Cms.Infrastructure.PublishedCache.Persistence; - -namespace Umbraco.Tests.Testing.Objects -{ - internal class TestDataSource : INuCacheContentService - { - private IPublishedModelFactory PublishedModelFactory { get; } = new NoopPublishedModelFactory(); - - public TestDataSource(params ContentNodeKit[] kits) - : this((IEnumerable) kits) - { } - - public TestDataSource(IEnumerable kits) => Kits = kits.ToDictionary(x => x.Node.Id, x => x); - - public Dictionary Kits { get; } - - // note: it is important to clone the returned kits, as the inner - // ContentNode is directly reused and modified by the snapshot service - public ContentNodeKit GetContentSource(int id) - => Kits.TryGetValue(id, out ContentNodeKit kit) ? kit.Clone(PublishedModelFactory) : default; - - public IEnumerable GetAllContentSources() - => Kits.Values - .OrderBy(x => x.Node.Level) - .ThenBy(x => x.Node.ParentContentId) - .ThenBy(x => x.Node.SortOrder) - .Select(x => x.Clone(PublishedModelFactory)); - - public IEnumerable GetBranchContentSources(int id) - => Kits.Values - .Where(x => x.Node.Path.EndsWith("," + id) || x.Node.Path.Contains("," + id + ",")) - .OrderBy(x => x.Node.Level) - .ThenBy(x => x.Node.ParentContentId) - .ThenBy(x => x.Node.SortOrder) - .Select(x => x.Clone(PublishedModelFactory)); - - public IEnumerable GetTypeContentSources(IEnumerable ids) - => Kits.Values - .Where(x => ids.Contains(x.ContentTypeId)) - .OrderBy(x => x.Node.Level) - .ThenBy(x => x.Node.ParentContentId) - .ThenBy(x => x.Node.SortOrder) - .Select(x => x.Clone(PublishedModelFactory)); - - public ContentNodeKit GetMediaSource(int id) => default; - - public IEnumerable GetAllMediaSources() => Enumerable.Empty(); - - public IEnumerable GetBranchMediaSources(int id) => Enumerable.Empty(); - - public IEnumerable GetTypeMediaSources(IEnumerable ids) => Enumerable.Empty(); - public void DeleteContentItem(IContentBase item) => throw new NotImplementedException(); - public void DeleteContentItems(IEnumerable items) => throw new NotImplementedException(); - public void RefreshContent(IContent content) => throw new NotImplementedException(); - public void RefreshEntity(IContentBase content) => throw new NotImplementedException(); - public bool VerifyContentDbCache() => throw new NotImplementedException(); - public bool VerifyMediaDbCache() => throw new NotImplementedException(); - public bool VerifyMemberDbCache() => throw new NotImplementedException(); - public void Rebuild(int groupSize = 5000, IReadOnlyCollection contentTypeIds = null, IReadOnlyCollection mediaTypeIds = null, IReadOnlyCollection memberTypeIds = null) => throw new NotImplementedException(); - } -} diff --git a/tests/Umbraco.Tests/Testing/UmbracoTestBase.cs b/tests/Umbraco.Tests/Testing/UmbracoTestBase.cs deleted file mode 100644 index 243e92ad537a..000000000000 --- a/tests/Umbraco.Tests/Testing/UmbracoTestBase.cs +++ /dev/null @@ -1,585 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Web.Routing; -using System.Xml.Linq; -using Examine; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; -using Moq; -using NUnit.Framework; -using Serilog; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Actions; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.ContentApps; -using Umbraco.Cms.Core.DependencyInjection; -using Umbraco.Cms.Core.Dictionary; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Install; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.IO.MediaPathSchemes; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Mail; -using Umbraco.Cms.Core.Manifest; -using Umbraco.Cms.Core.Mapping; -using Umbraco.Cms.Core.Media; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Net; -using Umbraco.Cms.Core.PropertyEditors; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Sections; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Serialization; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Services.Implement; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Core.Templates; -using Umbraco.Cms.Core.Trees; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure; -using Umbraco.Cms.Infrastructure.DependencyInjection; -using Umbraco.Cms.Infrastructure.Media; -using Umbraco.Cms.Infrastructure.Migrations.Install; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.Mappers; -using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; -using Umbraco.Cms.Infrastructure.Serialization; -using Umbraco.Cms.Tests.Common; -using Umbraco.Cms.Tests.Common.Testing; -using Umbraco.Extensions; -using Umbraco.Tests.TestHelpers; -using Umbraco.Tests.TestHelpers.Stubs; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web.Hosting; -using Umbraco.Web.Security; -using ILogger = Microsoft.Extensions.Logging.ILogger; - -namespace Umbraco.Tests.Testing -{ - /// - /// Provides the top-level base class for all Umbraco integration tests. - /// - /// - /// True unit tests do not need to inherit from this class, but most of Umbraco tests - /// are not true unit tests but integration tests requiring services, databases, etc. This class - /// provides all the necessary environment, through DI. Yes, DI is bad in tests - unit tests. - /// But it is OK in integration tests. - /// - public abstract class UmbracoTestBase - { - // this class - // ensures that Current is properly resetted - // ensures that a service container is properly initialized and disposed - // compose the required dependencies according to test options (UmbracoTestAttribute) - // - // everything is virtual (because, why not?) - // starting a test runs like this: - // - SetUp() // when overriding, call base.SetUp() *first* then setup your own stuff - // --- Compose() // when overriding, call base.Commpose() *first* then compose your own stuff - // --- Initialize() // same - // - test runs - // - TearDown() // when overriding, clear you own stuff *then* call base.TearDown() - // - // about attributes - // - // this class defines the SetUp and TearDown methods, with proper attributes, and - // these attributes are *inherited* so classes inheriting from this class should *not* - // add the attributes to SetUp nor TearDown again - // - // this class is *not* marked with the TestFeature attribute because it is *not* a - // test feature, and no test "base" class should be. only actual test feature classes - // should be marked with that attribute. - - protected IUmbracoBuilder Builder { get; private set; } - - protected IServiceProvider Factory { get; private set; } - - protected UmbracoTestAttribute Options { get; private set; } - - protected static bool FirstTestInSession { get; set; } = true; - - protected bool FirstTestInFixture { get; set; } = true; - - internal TestObjects TestObjects { get; private set; } - - private static TypeLoader _commonTypeLoader; - - private TypeLoader _featureTypeLoader; - - #region Accessors - protected ServiceContext ServiceContext => Factory.GetRequiredService(); - - protected ILoggerFactory LoggerFactory => Factory.GetRequiredService(); - - protected IJsonSerializer JsonNetSerializer { get; } = new JsonNetSerializer(); - - protected IIOHelper IOHelper { get; private set; } - protected UriUtility UriUtility => new UriUtility(HostingEnvironment); - protected IPublishedUrlProvider PublishedUrlProvider => Factory.GetRequiredService(); - protected IDataTypeService DataTypeService => Factory.GetRequiredService(); - protected IPasswordHasher PasswordHasher => Factory.GetRequiredService(); - protected Lazy PropertyEditorCollection => new Lazy(() => Factory.GetRequiredService()); - protected ILocalizationService LocalizationService => Factory.GetRequiredService(); - protected ILocalizedTextService LocalizedTextService { get; private set; } - protected IShortStringHelper ShortStringHelper => Factory?.GetRequiredService() ?? TestHelper.ShortStringHelper; - protected IImageUrlGenerator ImageUrlGenerator => Factory.GetRequiredService(); - protected UploadAutoFillProperties UploadAutoFillProperties => Factory.GetRequiredService(); - protected IUmbracoVersion UmbracoVersion { get; private set; } - - protected ITypeFinder TypeFinder { get; private set; } - - protected IProfiler Profiler => Factory.GetRequiredService(); - - protected virtual IProfilingLogger ProfilingLogger => Factory.GetRequiredService(); - - protected IHostingEnvironment HostingEnvironment { get; } = new AspNetHostingEnvironment(Microsoft.Extensions.Options.Options.Create(new HostingSettings())); - protected IApplicationShutdownRegistry HostingLifetime { get; } = new AspNetApplicationShutdownRegistry(); - protected IIpResolver IpResolver => Factory.GetRequiredService(); - protected IBackOfficeInfo BackOfficeInfo => Factory.GetRequiredService(); - protected AppCaches AppCaches => Factory.GetRequiredService(); - - protected virtual ISqlSyntaxProvider SqlSyntax => Factory.GetRequiredService(); - - protected IMapperCollection Mappers => Factory.GetRequiredService(); - - protected IUmbracoMapper Mapper => Factory.GetRequiredService(); - protected IHttpContextAccessor HttpContextAccessor => Factory.GetRequiredService(); - protected IContentService ContentService => Factory.GetRequiredService(); - protected IRuntimeState RuntimeState => MockRuntimeState(RuntimeLevel.Run); - private ILoggerFactory _loggerFactory; - - protected static IRuntimeState MockRuntimeState(RuntimeLevel level) - { - var runtimeState = Mock.Of(); - Mock.Get(runtimeState).Setup(x => x.Level).Returns(level); - return runtimeState; - } - #endregion - - #region Setup - - [SetUp] - public virtual void SetUp() - { - // should not need this if all other tests were clean - // but hey, never know, better avoid garbage-in - Reset(); - - // get/merge the attributes marking the method and/or the classes - Options = TestOptionAttributeBase.GetTestOptions(); - - // FIXME: align to runtimes & components - don't redo everything here !!!! Yes this is getting painful - - var loggerFactory = GetLoggerFactory(Options.Logger); - _loggerFactory = loggerFactory; - var profiler = new LogProfiler(loggerFactory.CreateLogger()); - var msLogger = loggerFactory.CreateLogger("msLogger"); - var proflogger = new ProfilingLogger(loggerFactory.CreateLogger(), profiler); - IOHelper = TestHelper.IOHelper; - - TypeFinder = new TypeFinder(loggerFactory.CreateLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, loggerFactory), new VaryingRuntimeHash()); - var appCaches = GetAppCaches(); - var globalSettings = new GlobalSettings(); - var settings = new WebRoutingSettings(); - - IBackOfficeInfo backOfficeInfo = new AspNetBackOfficeInfo(globalSettings, IOHelper, loggerFactory.CreateLogger(), Microsoft.Extensions.Options.Options.Create(settings)); - IIpResolver ipResolver = new AspNetIpResolver(); - UmbracoVersion = new UmbracoVersion(); - - - LocalizedTextService = new LocalizedTextService(new Dictionary>(), loggerFactory.CreateLogger()); - var typeLoader = GetTypeLoader(IOHelper, TypeFinder, appCaches.RuntimeCache, HostingEnvironment, loggerFactory.CreateLogger(), proflogger, Options.TypeLoader); - - var services = TestHelper.GetRegister(); - - Builder = new UmbracoBuilder(services, Mock.Of(), typeLoader); - - //TestHelper.GetConfigs().RegisterWith(register); - services.AddUnique(typeof(ILoggerFactory), loggerFactory); - services.AddTransient(typeof(ILogger<>), typeof(Logger<>)); - services.AddSingleton(msLogger); - services.AddUnique(IOHelper); - services.AddUnique(UriUtility); - services.AddUnique(UmbracoVersion); - services.AddUnique(TypeFinder); - services.AddUnique(LocalizedTextService); - services.AddUnique(typeLoader); - services.AddUnique(profiler); - services.AddUnique(proflogger); - services.AddUnique(appCaches); - services.AddUnique(HostingEnvironment); - services.AddUnique(backOfficeInfo); - services.AddUnique(ipResolver); - services.AddUnique(); - services.AddUnique(TestHelper.ShortStringHelper); - //services.AddUnique(); - - - var memberService = Mock.Of(); - var memberTypeService = Mock.Of(); - - TestObjects = new TestObjects(); - Compose(); - Current.Factory = Factory = TestHelper.CreateServiceProvider(Builder); - Initialize(); - } - - protected virtual void Compose() - { - ComposeMapper(Options.Mapper); - ComposeDatabase(Options.Database); - ComposeApplication(Options.WithApplication); - - // etc - ComposeWeb(); - ComposeMisc(); - - // not sure really - Compose(Builder); - } - - protected virtual void Compose(IUmbracoBuilder builder) - { } - - protected virtual void Initialize() - { - InitializeApplication(Options.WithApplication); - } - - #endregion - - #region Compose - - protected virtual ILoggerFactory GetLoggerFactory(UmbracoTestOptions.Logger option) - { - ILoggerFactory factory; - - switch (option) - { - case UmbracoTestOptions.Logger.Mock: - factory = NullLoggerFactory.Instance; - break; - case UmbracoTestOptions.Logger.Serilog: - factory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { builder.AddSerilog(); }); - break; - case UmbracoTestOptions.Logger.Console: - factory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { builder.AddConsole(); }); - break; - default: - throw new NotSupportedException($"Logger option {option} is not supported."); - } - - return factory; - } - - protected virtual AppCaches GetAppCaches() - { - return AppCaches.Disabled; - } - - protected virtual void ComposeWeb() - { - // imported from TestWithSettingsBase - // which was inherited by TestWithApplicationBase so pretty much used everywhere - Current.UmbracoContextAccessor = new TestUmbracoContextAccessor(); - - // web - Builder.Services.AddUnique(Current.UmbracoContextAccessor); - Builder.Services.AddUnique(Mock.Of()); - Builder.Services.AddUnique(); - Builder.WithCollectionBuilder(); - - - Builder.DataValueReferenceFactories(); - - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - Builder.SetCultureDictionaryFactory(); - Builder.Services.AddSingleton(f => f.GetRequiredService().CreateDictionary()); - // register back office sections in the order we want them rendered - Builder.WithCollectionBuilder().Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append() - .Append(); - Builder.Services.AddUnique(); - - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - - var webRoutingSettings = new WebRoutingSettings(); - Builder.Services.AddUnique(factory => - new UrlProvider( - factory.GetRequiredService(), - Microsoft.Extensions.Options.Options.Create(webRoutingSettings), - new UrlProviderCollection(Enumerable.Empty()), - new MediaUrlProviderCollection(Enumerable.Empty()), - factory.GetRequiredService())); - - - - } - - protected virtual void ComposeMisc() - { - // what else? - var runtimeStateMock = new Mock(); - runtimeStateMock.Setup(x => x.Level).Returns(RuntimeLevel.Run); - Builder.Services.AddUnique(f => runtimeStateMock.Object); - Builder.Services.AddTransient(_ => Mock.Of()); - Builder.Services.AddTransient(); - - // ah... - Builder.WithCollectionBuilder(); - Builder.WithCollectionBuilder(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - - Builder.Services.AddUnique(); - - // register empty content apps collection - Builder.WithCollectionBuilder(); - - // manifest - Builder.ManifestValueValidators(); - Builder.ManifestFilters(); - Builder.MediaUrlGenerators() - .Add() - .Add(); - - } - - protected virtual void ComposeMapper(bool configure) - { - if (configure == false) return; - - Builder - .AddCoreMappingProfiles(); - } - - protected virtual TypeLoader GetTypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, IHostingEnvironment hostingEnvironment, ILogger logger, IProfilingLogger profilingLogger, UmbracoTestOptions.TypeLoader option) - { - switch (option) - { - case UmbracoTestOptions.TypeLoader.Default: - return _commonTypeLoader ?? (_commonTypeLoader = CreateCommonTypeLoader(typeFinder, runtimeCache, logger, profilingLogger, hostingEnvironment)); - case UmbracoTestOptions.TypeLoader.PerFixture: - return _featureTypeLoader ?? (_featureTypeLoader = CreateTypeLoader(ioHelper, typeFinder, runtimeCache, logger, profilingLogger, hostingEnvironment)); - case UmbracoTestOptions.TypeLoader.PerTest: - return CreateTypeLoader(ioHelper, typeFinder, runtimeCache, logger, profilingLogger, hostingEnvironment); - default: - throw new ArgumentOutOfRangeException(nameof(option)); - } - } - - protected virtual TypeLoader CreateTypeLoader(IIOHelper ioHelper, ITypeFinder typeFinder, IAppPolicyCache runtimeCache, ILogger logger, IProfilingLogger profilingLogger, IHostingEnvironment hostingEnvironment) - { - return CreateCommonTypeLoader(typeFinder, runtimeCache, logger, profilingLogger, hostingEnvironment); - } - - // common to all tests = cannot be overriden - private static TypeLoader CreateCommonTypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, ILogger logger, IProfilingLogger profilingLogger, IHostingEnvironment hostingEnvironment) - { - return new TypeLoader(typeFinder, runtimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), logger, profilingLogger, false, new[] - { - Assembly.Load("Umbraco.Core"), - Assembly.Load("Umbraco.Web"), - Assembly.Load("Umbraco.Tests"), - Assembly.Load("Umbraco.Infrastructure"), - }); - } - - protected virtual void ComposeDatabase(UmbracoTestOptions.Database option) - { - if (option == UmbracoTestOptions.Database.None) return; - - // create the file - // create the schema - } - - protected virtual void ComposeSettings() - { - var contentSettings = new ContentSettings(); - var coreDebugSettings = new CoreDebugSettings(); - var globalSettings = new GlobalSettings(); - var nuCacheSettings = new NuCacheSettings(); - var requestHandlerSettings = new RequestHandlerSettings(); - var userPasswordConfigurationSettings = new UserPasswordConfigurationSettings(); - var webRoutingSettings = new WebRoutingSettings(); - - Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(contentSettings)); - Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(coreDebugSettings)); - Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(globalSettings)); - Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(nuCacheSettings)); - Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(requestHandlerSettings)); - Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(userPasswordConfigurationSettings)); - Builder.Services.AddTransient(x => Microsoft.Extensions.Options.Options.Create(webRoutingSettings)); - } - - protected virtual void ComposeApplication(bool withApplication) - { - ComposeSettings(); - - if (withApplication == false) return; - - // default Datalayer/Repositories/SQL/Database/etc... - Builder.AddRepositories(); - - Builder.Services.AddUnique(); - - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - - // register filesystems - Builder.Services.AddUnique(factory => TestObjects.GetFileSystemsMock()); - - - var scheme = Mock.Of(); - - var mediaFileManager = new MediaFileManager(Mock.Of(), scheme, Mock.Of>(), Mock.Of()); - Builder.Services.AddUnique(factory => mediaFileManager); - - // no factory (noop) - Builder.Services.AddUnique(); - - // register application stuff (database factory & context, services...) - Builder.WithCollectionBuilder() - .AddCoreMappers(); - - Builder.Services.AddUnique(_ => new TransientEventMessagesFactory()); - - var globalSettings = Microsoft.Extensions.Options.Options.Create(new GlobalSettings()); - var connectionStrings = Microsoft.Extensions.Options.Options.Create(new ConnectionStrings()); - - Builder.Services.AddUnique(f => new UmbracoDatabaseFactory(_loggerFactory.CreateLogger(), - LoggerFactory, - globalSettings, - connectionStrings, - new Lazy(f.GetRequiredService), - TestHelper.DbProviderFactoryCreator, - new DatabaseSchemaCreatorFactory(LoggerFactory.CreateLogger(), LoggerFactory, UmbracoVersion, Mock.Of()))); - - Builder.Services.AddUnique(f => f.GetService().SqlContext); - - Builder.WithCollectionBuilder(); // empty - - Builder.Services.AddUnique(factory - => TestObjects.GetScopeProvider(_loggerFactory, factory.GetService(), factory.GetService())); - Builder.Services.AddUnique(factory => (IScopeAccessor)factory.GetRequiredService()); - - Builder.AddServices(); - - // composition root is doing weird things, fix - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - - // somehow property editor ends up wanting this - Builder.WithCollectionBuilder(); - - Builder.Services.AddUnique(); - - // note - don't register collections, use builders - Builder.WithCollectionBuilder(); - Builder.Services.AddUnique(); - Builder.Services.AddUnique(); - - - Builder.Services.AddUnique(TestHelper.GetHttpContextAccessor(GetHttpContextFactory("/").HttpContext)); - } - - #endregion - - protected FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null) - { - var factory = routeData != null - ? new FakeHttpContextFactory(url, routeData) - : new FakeHttpContextFactory(url); - - return factory; - } - - #region Initialize - - protected virtual void InitializeApplication(bool withApplication) - { - if (withApplication == false) return; - - TestHelper.InitializeContentDirectories(); - } - - #endregion - - #region TearDown and Reset - - [TearDown] - public virtual void TearDown() - { - FirstTestInFixture = false; - FirstTestInSession = false; - - Reset(); - - if (Options.WithApplication) - { - TestHelper.CleanContentDirectories(); - TestHelper.CleanUmbracoSettingsConfig(); - } - } - - protected virtual void Reset() - { - try - { - // reset and dispose scopes - // ensures we don't leak an opened database connection - // which would lock eg SqlCe .sdf files - if (Factory?.GetService() is ScopeProvider scopeProvider) - { - Scope scope; - while ((scope = scopeProvider.AmbientScope) != null) - { - scope.Reset(); - scope.Dispose(); - } - } - } - catch (ObjectDisposedException ex) - { - if (!ex.ObjectName.Equals(nameof(IServiceProvider))) - throw; - } - - // reset all other static things that should not be static ;( - UriUtility.ResetAppDomainAppVirtualPath(HostingEnvironment); - } - - #endregion - } -} diff --git a/tests/Umbraco.Tests/Umbraco.Tests.csproj b/tests/Umbraco.Tests/Umbraco.Tests.csproj index 75f2e0a805b5..a83c0d64538a 100644 --- a/tests/Umbraco.Tests/Umbraco.Tests.csproj +++ b/tests/Umbraco.Tests/Umbraco.Tests.csproj @@ -135,170 +135,12 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -338,47 +180,10 @@ Umbraco.Web - - - ResXFileCodeGenerator - SqlResources.Designer.cs - Designer - - - ResXFileCodeGenerator - ImportResources.Designer.cs - Designer - - - ResXFileCodeGenerator - TestFiles.Designer.cs - Designer - - - - - Designer - Always - - - Always - - - - - - Designer - - - - - - - - + $(NuGetPackageFolders.Split(';')[0]) diff --git a/tests/Umbraco.Tests/Web/HttpCookieExtensionsTests.cs b/tests/Umbraco.Tests/Web/HttpCookieExtensionsTests.cs deleted file mode 100644 index 3964017e5709..000000000000 --- a/tests/Umbraco.Tests/Web/HttpCookieExtensionsTests.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; -using System.Threading.Tasks; -using NUnit.Framework; -using Umbraco.Web; - -namespace Umbraco.Tests.Web -{ - [TestFixture] - public class HttpCookieExtensionsTests - { - [TestCase("hello=world;cookies=are fun;", "hello", "world", true)] - [TestCase("HELlo=world;cookies=are fun", "hello", "world", true)] - [TestCase("HELlo= world;cookies=are fun", "hello", "world", true)] - [TestCase("HELlo =world;cookies=are fun", "hello", "world", true)] - [TestCase("hello = world;cookies=are fun;", "hello", "world", true)] - [TestCase("hellos=world;cookies=are fun", "hello", "world", false)] - [TestCase("hello=world;cookies?=are fun?", "hello", "world", true)] - [TestCase("hel?lo=world;cookies=are fun?", "hel?lo", "world", true)] - public void Get_Cookie_Value_From_HttpRequestHeaders(string cookieHeaderVal, string cookieName, string cookieVal, bool matches) - { - var request = new HttpRequestMessage(HttpMethod.Get, "http://test.com"); - var requestHeaders = request.Headers; - requestHeaders.Add("Cookie", cookieHeaderVal); - - var valueFromHeader = requestHeaders.GetCookieValue(cookieName); - - if (matches) - { - Assert.IsNotNull(valueFromHeader); - Assert.AreEqual(cookieVal, valueFromHeader); - } - else - { - Assert.IsNull(valueFromHeader); - } - } - } -} diff --git a/tests/Umbraco.Tests/Web/Mvc/ViewDataDictionaryExtensionTests.cs b/tests/Umbraco.Tests/Web/Mvc/ViewDataDictionaryExtensionTests.cs deleted file mode 100644 index 869fd1b329e0..000000000000 --- a/tests/Umbraco.Tests/Web/Mvc/ViewDataDictionaryExtensionTests.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Web.Mvc; -using NUnit.Framework; -using Umbraco.Web.Mvc; - -namespace Umbraco.Tests.Web.Mvc -{ - [TestFixture] - public class ViewDataDictionaryExtensionTests - { - [Test] - public void Merge_View_Data() - { - var source = new ViewDataDictionary(); - var dest = new ViewDataDictionary(); - source.Add("Test1", "Test1"); - dest.Add("Test2", "Test2"); - - dest.MergeViewDataFrom(source); - - Assert.AreEqual(2, dest.Count); - } - - [Test] - public void Merge_View_Data_Retains_Destination_Values() - { - var source = new ViewDataDictionary(); - var dest = new ViewDataDictionary(); - source.Add("Test1", "Test1"); - dest.Add("Test1", "MyValue"); - dest.Add("Test2", "Test2"); - - dest.MergeViewDataFrom(source); - - Assert.AreEqual(2, dest.Count); - Assert.AreEqual("MyValue", dest["Test1"]); - Assert.AreEqual("Test2", dest["Test2"]); - } - - } -} diff --git a/tests/Umbraco.Tests/masterpages/site1/template2.master b/tests/Umbraco.Tests/masterpages/site1/template2.master deleted file mode 100644 index f1a6da0d512b..000000000000 --- a/tests/Umbraco.Tests/masterpages/site1/template2.master +++ /dev/null @@ -1,5 +0,0 @@ -<%@ Master Language="C#" MasterPageFile="~/umbraco/masterpages/default.master" AutoEventWireup="true" %> - - - -