fix(gateway): resolve 2026-06-18 array-write review findings

- Server-057: extend []-suffix normalization to AddItemBulk/AddBufferedItem so bulk-added
  array tags bind write-capable handles (authz check, worker bind, and registration kept
  consistent); update gateway.md + client READMEs. Tests: AddItemBulk/AddBufferedItem wiring.
- Server-058: assert []-fallback-resolved bare array names are still denied when out of
  read/write scope and that MaxWriteClassification is enforced on suffixed array registrations.
- Contracts-023/024/025: round-trip + field-19 descriptor pin for MxSparseArray; document
  MxSparseArray in docs/Contracts.md; enumerate it in the protocol-version-3 test summary.
- Tests-040: add wiring tests for the six uncovered sparse-write arms (WriteSecured, Write2,
  WriteSecured2, Write2Bulk, WriteSecuredBulk, WriteSecured2Bulk).

dotnet build + targeted tests green (184 passed).
This commit is contained in:
Joseph Doherty
2026-06-18 10:58:42 -04:00
parent 6c853b43af
commit 2671639250
10 changed files with 718 additions and 30 deletions
@@ -1588,4 +1588,87 @@ public sealed class ProtobufContractRoundTripTests
Assert.Equal(150UL, parsed.ReplayGap.RequestedAfterSequence);
Assert.Equal(200UL, parsed.ReplayGap.OldestAvailableSequence);
}
/// <summary>
/// Pins the wire field number for <c>MxValue.sparse_array_value</c> (19)
/// via the generated constant, pins the <see cref="MxSparseArray"/> and
/// <see cref="MxSparseElement"/> field numbers by name + number against the
/// descriptor, and round-trips an <see cref="MxValue"/> carrying a
/// <see cref="MxSparseArray"/> with at least one <see cref="MxSparseElement"/>
/// so a future renumber or type-narrowing is caught at the contract level.
/// Also verifies that an all-defaults <see cref="MxSparseArray"/> (no elements)
/// is not a proto-level error. See Contracts-023.
/// </summary>
[Fact]
public void MxValue_RoundTripsSparseArrayValueAndPinsFieldNumbers()
{
// Pin MxValue.sparse_array_value wire field number == 19.
Assert.Equal(19, MxValue.SparseArrayValueFieldNumber);
// Pin MxSparseArray field numbers by name + number.
var sparseArrayFields = MxSparseArray.Descriptor.Fields;
Assert.Equal(1, sparseArrayFields[MxSparseArray.ElementDataTypeFieldNumber].FieldNumber);
Assert.Equal("element_data_type", sparseArrayFields[MxSparseArray.ElementDataTypeFieldNumber].Name);
Assert.Equal(2, sparseArrayFields[MxSparseArray.TotalLengthFieldNumber].FieldNumber);
Assert.Equal("total_length", sparseArrayFields[MxSparseArray.TotalLengthFieldNumber].Name);
Assert.Equal(3, sparseArrayFields[MxSparseArray.ElementsFieldNumber].FieldNumber);
Assert.Equal("elements", sparseArrayFields[MxSparseArray.ElementsFieldNumber].Name);
// Pin MxSparseElement field numbers by name + number.
var sparseElementFields = MxSparseElement.Descriptor.Fields;
Assert.Equal(1, sparseElementFields[MxSparseElement.IndexFieldNumber].FieldNumber);
Assert.Equal("index", sparseElementFields[MxSparseElement.IndexFieldNumber].Name);
Assert.Equal(2, sparseElementFields[MxSparseElement.ValueFieldNumber].FieldNumber);
Assert.Equal("value", sparseElementFields[MxSparseElement.ValueFieldNumber].Name);
// Round-trip an MxValue with a populated MxSparseArray (one scalar element).
var original = new MxValue
{
DataType = MxDataType.Float,
SparseArrayValue = new MxSparseArray
{
ElementDataType = MxDataType.Float,
TotalLength = 4,
Elements =
{
new MxSparseElement
{
Index = 2,
Value = new MxValue
{
DataType = MxDataType.Float,
FloatValue = 3.14f,
VariantType = "VT_R4",
},
},
},
},
};
var parsed = MxValue.Parser.ParseFrom(original.ToByteArray());
Assert.Equal(original, parsed);
Assert.Equal(MxValue.KindOneofCase.SparseArrayValue, parsed.KindCase);
Assert.NotNull(parsed.SparseArrayValue);
Assert.Equal(MxDataType.Float, parsed.SparseArrayValue.ElementDataType);
Assert.Equal(4u, parsed.SparseArrayValue.TotalLength);
var element = Assert.Single(parsed.SparseArrayValue.Elements);
Assert.Equal(2u, element.Index);
Assert.Equal(3.14f, element.Value.FloatValue);
// An all-defaults MxSparseArray (no elements) is not a proto-level error.
var empty = new MxValue
{
DataType = MxDataType.Float,
SparseArrayValue = new MxSparseArray
{
ElementDataType = MxDataType.Float,
TotalLength = 10,
},
};
var parsedEmpty = MxValue.Parser.ParseFrom(empty.ToByteArray());
Assert.Equal(empty, parsedEmpty);
Assert.Equal(MxValue.KindOneofCase.SparseArrayValue, parsedEmpty.KindCase);
Assert.Empty(parsedEmpty.SparseArrayValue.Elements);
}
}