Reference

Trax.Api.GraphQL.Testing

Package Trax.Api.GraphQL.TestingNamespace Trax.Api.GraphQL.TestingSource CrossSchemaGuards.cs, CrossSchemaGuardFixture.cs

The GraphQL guard package: checkers for the cross-schema GraphQL pattern, and an NUnit fixture that runs them. It depends on Trax.Core.Testing and Trax.Api.GraphQL.

Signatures

namespace Trax.Api.GraphQL.Testing;
 
public static class CrossSchemaGuards
{
    public static GuardResult EdgeManifestIsValid(IReadOnlyList<CrossSchemaEdge> edges);
    public static GuardResult EdgeResolversUseLoader(ArchitectureGuardOptions options, string crossSchemaProjectSuffix = ".CrossSchema");
    public static GuardResult ExtensionResolversDeclareParentRequirements(ArchitectureGuardOptions options);
}
 
[TestFixture]
public abstract class CrossSchemaGuardFixture
{
    protected virtual ArchitectureGuardOptions Options { get; }
    protected virtual IReadOnlyList<CrossSchemaEdge> Edges { get; }
    protected virtual bool ExpectsCrossSchemaResolvers { get; }
    protected virtual bool ExpectsParentResolvers { get; }
 
    [Test] public void Cross_schema_edge_manifest_is_valid();
    [Test] public void Cross_schema_edge_resolvers_use_the_batched_loader();
    [Test] public void Extension_resolvers_declare_what_they_read_off_their_parent();
 
    protected static void AssertInspected(GuardResult result, bool expected, string what, string optOut, ArchitectureGuardOptions options);
}

Members

CrossSchemaGuards

CheckerChecks
EdgeManifestIsValid(edges)Each CrossSchemaEdge names a real integer foreign key on its source, a target owned as a DbSet by the declared target context, and a camelCase field name
EdgeResolversUseLoader(options, crossSchemaProjectSuffix)Every resolver on a type extension in a project whose name ends in crossSchemaProjectSuffix routes through a CrossSchemaLoader<,>: a public method of an [ExtendObjectType] class, or of a source-generated [ObjectType<T>] extension, that is not [GraphQLIgnore] and names CrossSchemaLoader<,> in its parameters or body
ExtensionResolversDeclareParentRequirements(options)Every property a [Parent] resolver reads off its parent, other than the key Trax adds itself, is declared in [Parent(requires: ...)]

CrossSchemaGuardFixture

MemberDefaultDescription
Optionsnew()Scan roots and allowlists
EdgesemptyThe edge manifest; empty passes the manifest check
ExpectsCrossSchemaResolverstrueWhether the scan roots must hold at least one cross-schema [ExtendObjectType] resolver
ExpectsParentResolverstrueWhether they must hold at least one [Parent] resolver
Cross_schema_edge_manifest_is_validRuns EdgeManifestIsValid(Edges); fails on any offender
Cross_schema_edge_resolvers_use_the_batched_loaderRuns EdgeResolversUseLoader(Options); fails when it inspected nothing and ExpectsCrossSchemaResolvers is true, then on any offender
Extension_resolvers_declare_what_they_read_off_their_parentRuns ExtensionResolversDeclareParentRequirements(Options); fails when it inspected nothing and ExpectsParentResolvers is true, then on any offender
AssertInspected(result, expected, what, optOut, options)The shared "inspected nothing" failure, naming the scan roots and the property that opts out. Protected, for a subclass's own guards.

Examples

using Trax.Api.GraphQL.Testing;
using Trax.Core.Testing;
 
var result = CrossSchemaGuards.ExtensionResolversDeclareParentRequirements(
    new ArchitectureGuardOptions { SourceScanRoots = ["src"] }
);
 
if (result.Inspected == 0 || !result.Passed)
    throw new InvalidOperationException(result.FailureMessage);

The fixture, subclassed in an NUnit test project:

[TestFixture]
public sealed class CrossSchemaGuards : CrossSchemaGuardFixture
{
    protected override ArchitectureGuardOptions Options => new() { SourceScanRoots = ["libs"] };
    protected override IReadOnlyList<CrossSchemaEdge> Edges => MyCrossSchemaEdges.All;
}

See also