Skip to content
bitzorcas
中EN

Reference

Architecture tests

Enforce dependency direction, module seams, generators, commercial delivery, and deletion gates through assemblies, projects, source contracts, and manifests.

Last updated

Architecture tests turn “how the code should be organized” into an executable merge contract. BitzOrcas.Architecture.Tests does more than inspect assembly references: it parses .csproj, source, JSON, YAML, and architecture documents to stop old mappers, runtime reflection, invalid cross-module references, stale commercial catalogs, and deleted shallow modules from returning.

Position in the evidence chain

failpass

ADR / architecture specification

Decidable rule

Assembly dependency assertion

Project and source contract

Manifest / CI contract

Architecture.Tests

Fix implementation or update decision

Continue through quality gates

These tests prove structure still conforms to accepted design. They do not prove SQL, transaction, or messaging behavior on real infrastructure; integration contracts own that evidence. Conversely, a working API path does not prove dependency direction remains sound.

Current protection surface

SurfaceTypical factPrimary evidence
LayeringDomain does not depend on Application/Infrastructure/Apiassemblies, .csproj
Module collaborationcross-module calls use approved Contracts seamsproject graph, exception registry
Persistenceno ORM in Application; aggregate and metadata rules holdproject XML, source, generated manifest
AOTunregistered runtime-reflection paths remain bannedsource scan, trim-gate contract
GeneratorsDI, endpoints, queries, persistence metadata are compile-time outputsgenerator source and consumption tests
Commercial deliverycatalog, Profile closure, packable projects agreeJSON, project properties, directory scan
Consumertemplates do not copy core source or reference the product repositoryisolated consumer and source contracts
CIjobs, triggers, timeout, Docker shards, GA fail-closed behavior remain explicitworkflow YAML contract
Deletion gatesshallow modules, old mappers, old query reflection cannot returnglobal path and token sweep

The repository has risk-focused classes such as CsprojDependencyGraphTests, ReflectionAotGuardTests, CommercialPackageCatalogTests, DockerContractTraitTests, and ShallowModuleDeletionGateTests. Extending an existing owner is easier to maintain than creating a catch-all test class.

Three rule implementations

Compiled dependency rules

Assembly rules answer “what does the produced binary actually reference?” Workflow rules load BitzOrcas.Workflow.Engine and assert that it does not reference SqlSugar, EF Core, Dapper, ASP.NET Core, or a Workflow adapter. That evidence is closer to the deliverable than source using statements.

[Theory]
[InlineData("BitzOrcas.Workflow.Engine")]
public void WorkflowEngine_ShouldNotReferenceAdapters(string assemblyName)
{
// Inspect direct references in the compiled artifact, not source imports.
var references = Assembly.Load(assemblyName)
.GetReferencedAssemblies()
.Select(item => item.Name)
.ToArray();
// Keep forbidden edges explicit so failures identify the broken boundary.
references.ShouldNotContain("SqlSugar");
references.ShouldNotContain("Microsoft.EntityFrameworkCore");
references.ShouldNotContain("BitzOrcas.Workflow.EfCore");
}

Project graph and machine-readable contracts

CsprojDependencyGraphTests parses applicable .csproj files, distinguishes ProjectReference from PackageReference, and reads an explicit exception registry. It catches an Application referencing an ORM package, Contracts pointing back into implementation, or module A depending directly on module B’s Application.

var project = XDocument.Load(projectPath);
// Internal project edges reveal layering; package edges reveal ORM leakage.
var projectReferences = project.Descendants("ProjectReference")
.Select(node => node.Attribute("Include")?.Value)
.Where(value => value is not null)
.ToArray();
// This assertion owns Application-to-Infrastructure; separate rules own other layers.
projectReferences.ShouldNotContain(path =>
path!.Contains(".Infrastructure", StringComparison.Ordinal));

Source and deletion gates

Some contracts cannot be reconstructed from assemblies: a command must use a transition template, a retired directory must not exist, or CI must retain thirteen Docker shards. Those rules inspect source or workflow files and report full offender paths. Source tokens are appropriate for explicit, stable structure—not for inferring complicated behavior.

Run and narrow the suite

Terminal window
# Run the complete architecture contract before merge.
dotnet test tests/BitzOrcas.Architecture.Tests --configuration Release
# Verify only CI quality-gate contracts; class filters are more stable.
dotnet test tests/BitzOrcas.Architecture.Tests \
--configuration Release \
--filter 'FullyQualifiedName~CiQualityGateTests'

When a rule consumes Release artifacts, do not explain it with stale bin/Debug output. Use --no-build only after building the same commit and configuration.

Add a rule end to end

  1. State the regression precisely, such as “Platform Application must not PackageReference an ORM,” not “keep architecture clean.”
  2. Locate the authoritative ADR, constraint, or machine-readable catalog; write the decision first if none exists.
  3. Choose evidence closest to the fact: assembly, project XML, manifest, source path, or CI YAML.
  4. Make the test fail against a violating fixture or temporary change so it cannot be permanently green.
  5. Report every offender, the reason, and the correction path rather than returning only false.
  6. Register any real exception with an owner, rationale, and exit condition; never allow an entire directory.
  7. Run the focused rule, all Architecture.Tests, and the affected business tests.

Diagnose failures

Start with the failing class name; it normally identifies ownership. For project-graph failures inspect the offending .csproj and exception registry. For source gates determine whether a path moved or semantics changed. For manifests compare both producer and consumer. Change specification, test, and implementation together only when the architecture decision truly changed.

Review checklist

  • Rule names express an architectural fact, not an implementation action.
  • Scan scope includes new modules and documents exclusions explicitly.
  • Failure output lists every offender and the authoritative rule.
  • Windows/Linux separators and case differences are handled.
  • Exceptions are exact entries, never permanent directory exemptions.
  • Structural tests do not impersonate runtime, performance, or security evidence.
  • A global rg/find sweep follows the local change; expected old-pattern count is zero.

See also

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%