Class ServiceExtensionCreatorValidationTests
EditMode tests for Validate(Input) and its pure preview helpers
(ComposeServiceTypeName(Input) / ComposeSanitizedExtensionName(Input)). Aliased
as Creator because the class name collides with its own namespace segment
(Serenity.ServiceExtensionCreator.Editor.ServiceExtensionCreator). Every case that needs a real
aggregate uses "Combo" (gameplay, virtualized, no InterfaceAdapters layer) from the live
AggregateCatalog. Validate only reads the target folder (via
Serenity.Global.Infrastructure.Editor.SerenityProjectOutputFolderRules.IsAcceptableOutputFolder(System.String), which accepts a
well-formed folder whether or not it exists yet) and never writes into it, so one folder is created
for the whole fixture instead of per-test — nothing here ever mutates it, and a shared folder means
no repeated create/delete churn that could race an external file watcher (AV/cloud-sync).
Inherited Members
Namespace: Serenity.ServiceExtensionCreator.Tests.Editor
Assembly: Serenity.ServiceExtensionCreator.Tests.Editor.dll
Syntax
public class ServiceExtensionCreatorValidationTests : MetaFileRaceGuardedFixture
Constructors
ServiceExtensionCreatorValidationTests()
Declaration
public ServiceExtensionCreatorValidationTests()
Methods
ComposeCodeLayerName_InfrastructureFresh_ResolvesToInfrastructureRegardlessOfBaseLayer()
The generated class for InfrastructureFresh lives under Infrastructure, exactly like InfrastructureSubclass — ComposeCodeLayerName(Input) is style-driven, not BaseLayer-driven, for the two Infrastructure styles.
Declaration
public void ComposeCodeLayerName_InfrastructureFresh_ResolvesToInfrastructureRegardlessOfBaseLayer()
ComposeServiceTypeName_BusinessStyle_DoesNotStripUnityPrefix()
Declaration
public void ComposeServiceTypeName_BusinessStyle_DoesNotStripUnityPrefix()
ComposeServiceTypeName_InfrastructureStyle_StripsSingleLeadingUnityPrefix()
Declaration
public void ComposeServiceTypeName_InfrastructureStyle_StripsSingleLeadingUnityPrefix()
ComposeServiceTypeName_NullInput_ReturnsEmpty()
Declaration
public void ComposeServiceTypeName_NullInput_ReturnsEmpty()
ComposeServiceTypeName_SubclassOfNonUnityBase_KeepsTheUserUnityPrefix()
The double-prefix guard only strips a leading "Unity" when the composed name injects one of its
own — Persistence's only eligible base is the non-Unity-prefixed PersistenceService, so a
user extension name starting with "Unity" must survive intact.
Declaration
public void ComposeServiceTypeName_SubclassOfNonUnityBase_KeepsTheUserUnityPrefix()
ComposeServiceTypeName_SubclassWithEmptyBaseName_ResolvesTheCanonicalBase()
An empty SubclassBaseTypeName picks the canonical/first
eligible base — the composed name stays exactly V1's {Name}Unity{Aggregate}Service shape.
Declaration
public void ComposeServiceTypeName_SubclassWithEmptyBaseName_ResolvesTheCanonicalBase()
ComposeTypeName_PlainServiceStyle_SanitizedNameAlreadyEndsInAggregateService_DoesNotDoubleTheSuffix()
Same doubling guard, for the plain (non-Subclass) Service composition
({Name}{Aggregate}Service) — a sanitized name already ending in "{Aggregate}Service" must not
double either.
Declaration
public void ComposeTypeName_PlainServiceStyle_SanitizedNameAlreadyEndsInAggregateService_DoesNotDoubleTheSuffix()
ComposeTypeName_SanitizedNameAlreadyEndsInConcreteBaseName_DoesNotDoubleTheSuffix()
Regression test for the reported doubling bug: when the user-typed extension name already ends in the chosen concrete base's type name (e.g. "CustomAssetPrefetcherService" against a base literally named "AssetPrefetcherService"), the composed name must not double the suffix into "CustomAssetPrefetcherServiceAssetPrefetcherService".
Declaration
public void ComposeTypeName_SanitizedNameAlreadyEndsInConcreteBaseName_DoesNotDoubleTheSuffix()
ComposeTypeName_SubclassStyleWithNonCanonicalBase_ComposesNameFromTheChosenBase()
The Subclass type name is composed from the ACTUAL chosen base's name, not an invented
Unity{Aggregate}Service — a non-canonical base like the fixture CustomComboBase
composes {Name}CustomComboBase. Constructed-descriptor test: no production aggregate has a
non-canonical eligible base yet, so the descriptor is built directly (ComposeTypeName is
pure and never reads the catalog).
Declaration
public void ComposeTypeName_SubclassStyleWithNonCanonicalBase_ComposesNameFromTheChosenBase()
OneTimeSetUp()
Declaration
public void OneTimeSetUp()
OneTimeTearDown()
Declaration
public void OneTimeTearDown()
ValidateSubclassBaseSelection_SimpleNameSharedByTwoBases_DemandsTheFullName()
The base-selection resolver's ambiguity rule, pinned at the unit level with a constructed list — two fixture types sharing a simple name cannot both be matched by it, so the resolver demands the full name instead of first-picking.
Declaration
public void ValidateSubclassBaseSelection_SimpleNameSharedByTwoBases_DemandsTheFullName()
Validate_AllowsBusinessDecoratorStyle()
Declaration
public void Validate_AllowsBusinessDecoratorStyle()
Validate_AllowsBusinessFreshForPersistence()
Business styles do not need a concrete Unity service, so Persistence must remain usable.
Declaration
public void Validate_AllowsBusinessFreshForPersistence()
Validate_AllowsEmptyNamespace()
Declaration
public void Validate_AllowsEmptyNamespace()
Validate_AllowsInfrastructureFreshForAggregateWithoutVirtualMembers()
InfrastructureFresh is never gated on IsInfrastructureAvailable(AggregateDescriptor, out string) — unlike InfrastructureSubclass (see Validate_RejectsInfrastructureForAggregateWithoutVirtualMembers()), it must still validate cleanly for an aggregate with no virtual members at all.
Declaration
public void Validate_AllowsInfrastructureFreshForAggregateWithoutVirtualMembers()
Validate_AllowsInfrastructureFreshForPersistence()
InfrastructureFresh needs no concrete Unity service at all (unlike
InfrastructureSubclass) — Persistence (no UnityPersistenceService,
see Discover_Persistence_IsCoreInstalledWithNoConcreteUnityService())
must still validate cleanly under it.
Declaration
public void Validate_AllowsInfrastructureFreshForPersistence()
Validate_AllowsInfrastructureSubclassForPersistenceViaTheBusinessPersistenceService()
Persistence has no UnityPersistenceService, but the public business PersistenceService
implements IPersistenceService with five public virtual operations — under the
any-eligible-base rule it IS a valid subclass base, so the Subclass style is now available for
Persistence (it was blocked when only the canonical Unity{X}Service counted). The composed
type name follows the actual base: {Name}PersistenceService, no "Unity" prefix invented.
Declaration
public void Validate_AllowsInfrastructureSubclassForPersistenceViaTheBusinessPersistenceService()
Validate_AllowsInfrastructureSubclassForVirtualizedAggregate()
Declaration
public void Validate_AllowsInfrastructureSubclassForVirtualizedAggregate()
Validate_AllowsSubclassWithTheCanonicalBaseNamedExplicitly()
Declaration
public void Validate_AllowsSubclassWithTheCanonicalBaseNamedExplicitly()
Validate_AllowsValidDottedNamespace()
Declaration
public void Validate_AllowsValidDottedNamespace()
Validate_OutputFolderMissing_IsAccepted()
Declaration
public void Validate_OutputFolderMissing_IsAccepted()
Validate_RejectsEmptyName()
Declaration
public void Validate_RejectsEmptyName()
Validate_RejectsExcludedAggregate()
Declaration
public void Validate_RejectsExcludedAggregate()
Validate_RejectsFolderOutsideAssets()
Declaration
public void Validate_RejectsFolderOutsideAssets()
Validate_RejectsInfrastructureForAggregateWithoutVirtualMembers()
Declaration
public void Validate_RejectsInfrastructureForAggregateWithoutVirtualMembers()
Validate_RejectsNamespaceInjectionPayload()
Declaration
public void Validate_RejectsNamespaceInjectionPayload()
Validate_RejectsNamespaceSegmentStartingWithDigit()
Declaration
public void Validate_RejectsNamespaceSegmentStartingWithDigit()
Validate_RejectsNamespaceWithLeadingDot()
Declaration
public void Validate_RejectsNamespaceWithLeadingDot()
Validate_RejectsNamespaceWithTrailingDot()
Declaration
public void Validate_RejectsNamespaceWithTrailingDot()
Validate_RejectsNullInput()
Declaration
public void Validate_RejectsNullInput()
Validate_RejectsTypeNameCollisionWithExistingRealType()
Validate's collision check compares against the layered namespace it will actually generate into
({RootNamespace}.{CodeLayer}.Services), not the raw NamespaceOverride — so this test
collides against a fixture type declared right below, in this same file, whose namespace was chosen
to be exactly the composed namespace for the input below, rather than depending on a real Serenity
aggregate's namespace shape (which no longer has any reason to match the generated layout).
Declaration
public void Validate_RejectsTypeNameCollisionWithExistingRealType()
Validate_RejectsUnknownAggregate()
Declaration
public void Validate_RejectsUnknownAggregate()
Validate_RejectsUnknownSubclassBaseNameListingTheAvailableBases()
Declaration
public void Validate_RejectsUnknownSubclassBaseNameListingTheAvailableBases()