Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c9b6788
feat(policies): page caps per declared purpose
Sajadh92 Sep 24, 2026
774cf59
fix(policies): a path through a member a constructor builds is refuse…
Sajadh92 Sep 24, 2026
37bde0d
fix(policies): the parts of a struct take the struct member's policy
Sajadh92 Sep 24, 2026
c92be42
fix(core): a typed selection through a struct returns its values
Sajadh92 Sep 24, 2026
2b14618
fix(core): a typed selection beneath a collection of structs carries …
Sajadh92 Sep 24, 2026
225b537
chore(release): 3.4.0 in all four packages, with release notes
Sajadh92 Sep 24, 2026
6491a3c
fix(policies): a transform on a struct's member is applied to the row
Sajadh92 Sep 25, 2026
b4cfe06
docs(release): the struct transform fix in the 3.4.0 notes
Sajadh92 Sep 25, 2026
1ffad2d
fix(policies): a synthesized projection narrows a struct instead of l…
Sajadh92 Sep 25, 2026
1467166
docs(release): struct narrowing in the 3.4.0 notes
Sajadh92 Sep 25, 2026
dea066c
refactor(policies): no copied parameter in GraphWalker.Transform
Sajadh92 Sep 25, 2026
fcf2fd9
fix(policies): a member of a nullable struct is policed as the policy…
Sajadh92 Sep 25, 2026
992903c
fix(policies): Bind reports a refused value as the InvalidOperationEx…
Sajadh92 Sep 25, 2026
117d1cc
docs(release): nullable structs, the list-initializer limit and exact…
Sajadh92 Sep 25, 2026
866a1f1
fix(policies): Bind finds the setter's refusal wherever the binder wr…
Sajadh92 Sep 25, 2026
5f86803
docs(release): the parts of a struct are a security fix in the 3.4.0 …
Sajadh92 Sep 25, 2026
9bb8eb9
docs: 3.4.0 across the reference, the guide, the README and the site
Sajadh92 Sep 25, 2026
9a02dba
fix(policies): the gate adds and gates only the key a builder adds, a…
Sajadh92 Sep 25, 2026
cc2e34b
docs: a guarded selection through a struct carries only what was named
Sajadh92 Sep 25, 2026
b543b25
fix(policies): under Strict a clause on a constructed member itself i…
Sajadh92 Sep 25, 2026
7fb0e26
docs: a clause on a constructed member itself is refused, and a whole…
Sajadh92 Sep 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions DynamicWhere.Tests/Policies/ConfigureOnceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ private static DwPolicyOptions Copy()
copy.Caps.MinGroupSize = inForce.Caps.MinGroupSize;
}

foreach (KeyValuePair<string, DwPageCaps> purpose in inForce.Caps.Purposes)
{
copy.Caps.Purposes[purpose.Key] = new DwPageCaps
{
MaxPageSize = purpose.Value.MaxPageSize,
DefaultPageSize = purpose.Value.DefaultPageSize
};
}

foreach (KeyValuePair<Type, string> exposed in inForce.Entities.Entities)
{
copy.Entities.Expose(exposed.Key, exposed.Value);
Expand Down Expand Up @@ -446,6 +455,83 @@ public void Every_value_on_the_posture_is_compared(Type declaring)
}
}

[Fact]
public void A_purpose_whose_page_caps_are_the_deployment_ones_is_the_same_posture()
{
// Compared by the caps that apply: a purpose writing out the deployment's numbers enforces
// what no entry enforces.
DwPolicyOptions same = Copy();

same.Caps.Purposes["zz-same"] = new DwPageCaps
{
MaxPageSize = DwPolicy.Options.Caps.MaxPageSize,
DefaultPageSize = DwPolicy.Options.Caps.DefaultPageSize == 0 ? null : DwPolicy.Options.Caps.DefaultPageSize
};

DwPolicyOptions inForce = DwPolicy.Options;

DwPolicy.Configure(same);

Assert.Same(inForce, DwPolicy.Options);
}

[Fact]
public void A_purpose_with_other_page_caps_is_a_different_posture()
{
DwPolicyOptions larger = Copy();
larger.Caps.Purposes["zz-export"] = new DwPageCaps { MaxPageSize = DwPolicy.Options.Caps.MaxPageSize + 1 };

Refused(larger);

DwPolicyOptions defaulted = Copy();
defaulted.Caps.Purposes["zz-export"] = new DwPageCaps { DefaultPageSize = DwPolicy.Options.Caps.DefaultPageSize + 1 };

Refused(defaulted);
}

/// <summary>
/// Every property of the caps either sets a value the comparison walks above, or is one of the
/// two named here; and every value a purpose holds is compared.
/// </summary>
/// <remarks>
/// The reflection walk above reads settable properties, and the purposes are a collection with no
/// setter, so this is what keeps a later collection on the caps from being forgotten the same way.
/// </remarks>
[Fact]
public void Every_page_cap_a_purpose_holds_is_compared()
{
List<string> unsettable = typeof(DwCaps)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(property => property.GetSetMethod() is null)
.Select(property => property.Name)
.OrderBy(name => name, StringComparer.Ordinal)
.ToList();

Assert.Equal(new[] { nameof(DwCaps.IsMinGroupSizeSet), nameof(DwCaps.Purposes) }, unsettable);

List<PropertyInfo> held = typeof(DwPageCaps)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(property => property.GetSetMethod() is not null)
.ToList();

Assert.NotEmpty(held);

foreach (PropertyInfo property in held)
{
DwPolicyOptions different = Copy();
DwPageCaps caps = new();

property.SetValue(caps, 7);
different.Caps.Purposes["zz-" + property.Name] = caps;

// Seven differs from both deployment caps the bootstrap posture holds.
Assert.NotEqual(7, DwPolicy.Options.Caps.MaxPageSize);
Assert.NotEqual(7, DwPolicy.Options.Caps.DefaultPageSize);

Refused(different);
}
}

/// <summary>A value of the same type that is not the one held.</summary>
private static object? Other(object? held, Type type) => type switch
{
Expand Down
Loading
Loading