diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.IntegrationTests/SqlServerReadTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.IntegrationTests/SqlServerReadTests.cs
index ebf2d212..ff6271bc 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.IntegrationTests/SqlServerReadTests.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Sql.IntegrationTests/SqlServerReadTests.cs
@@ -58,6 +58,64 @@ public sealed class SqlServerReadTests
driver.GetHostStatuses().ShouldHaveSingleItem().State.ShouldBe(HostState.Running);
}
+ // ---- R06:S-1: the row cap, on real T-SQL ----
+
+ ///
+ /// A where-pair selector pointed at an events table (40 rows for one station) used to pull every
+ /// matching row on every poll. The plan now emits SELECT TOP (2) … — proven valid T-SQL here —
+ /// and the second row makes the tag publish BadConfigurationError while the driver stays
+ /// Healthy (the server answered). The positive control: a station with exactly one row reads Good
+ /// through the same capped statement.
+ ///
+ [Fact]
+ public async Task ReadAsync_realSqlServer_anAmbiguousSelectorIsRowCappedToAConfigurationError()
+ {
+ SkipUnlessLive();
+ const string table = "RowCapEvents";
+
+ await using (var arrange = await _sql.OpenInspectionConnectionAsync())
+ {
+ await using var command = arrange.CreateCommand();
+ command.CommandText = string.Create(CultureInfo.InvariantCulture, $"""
+ IF OBJECT_ID(N'{SqlPollServerFixture.Schema}.{table}') IS NOT NULL DROP TABLE {SqlPollServerFixture.Schema}.{table};
+ CREATE TABLE {SqlPollServerFixture.Schema}.{table} (
+ {SqlPollServerFixture.WideRowSelectorColumn} int NOT NULL,
+ oven_temp float NULL,
+ {SqlPollServerFixture.TimestampColumn} datetime2 NOT NULL);
+ INSERT INTO {SqlPollServerFixture.Schema}.{table} VALUES (1, 21.5, '2026-07-24T10:00:00');
+ DECLARE @i int = 0;
+ WHILE @i < 40
+ BEGIN
+ INSERT INTO {SqlPollServerFixture.Schema}.{table} VALUES (7, @i, DATEADD(second, @i, '2026-07-24T10:00:00'));
+ SET @i += 1;
+ END
+ """);
+ await command.ExecuteNonQueryAsync(TestContext.Current.CancellationToken);
+ }
+
+ try
+ {
+ await using var driver = await StartAsync(
+ WideRowWhere("Sql/Events/Ambiguous", "oven_temp", "7", table),
+ WideRowWhere("Sql/Events/Clean", "oven_temp", "1", table));
+
+ var snapshots = await driver.ReadAsync(
+ ["Sql/Events/Ambiguous", "Sql/Events/Clean"], TestContext.Current.CancellationToken);
+
+ snapshots[0].StatusCode.ShouldBe(SqlStatusCodes.BadConfigurationError);
+ snapshots[1].StatusCode.ShouldBe(SqlStatusCodes.Good);
+ Convert.ToDouble(snapshots[1].Value).ShouldBe(21.5);
+ driver.GetHealth().State.ShouldBe(DriverState.Healthy);
+ }
+ finally
+ {
+ await using var cleanup = await _sql.OpenInspectionConnectionAsync();
+ await using var drop = cleanup.CreateCommand();
+ drop.CommandText = $"DROP TABLE IF EXISTS {SqlPollServerFixture.Schema}.{table}";
+ await drop.ExecuteNonQueryAsync(CancellationToken.None);
+ }
+ }
+
// ---- key-value model ----
/// The plan's headline case: a seeded key-value row round-trips as a Good snapshot.