Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion Darling/Darling.Tests/DarlingMcpHealthToolsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,10 @@ deadlock row surviving there means the purge has not reached the day (data_state
Assert.Contains("\"deadlock_count\":1", onItsOwnDay, StringComparison.Ordinal);
var judged = JsonDocument.Parse(onItsOwnDay).RootElement;
Assert.Equal("past_horizon", judged.GetProperty("data_state").GetString());
Assert.Equal("No Data", judged.GetProperty("overall_health").GetString());
/* #3653: one band, one spelling — overall_health carries the token health_band does ("NoData", not the
viewer label "No Data"). */
Assert.Equal("NoData", judged.GetProperty("overall_health").GetString());
Assert.Equal("NoData", judged.GetProperty("health_band").GetString());
Assert.Contains("1 of 7 signal sources", judged.GetProperty("data_note").GetString(), StringComparison.Ordinal);
Assert.DoesNotContain("Healthy", onItsOwnDay, StringComparison.Ordinal);
Assert.Contains("2026-07-20", onItsOwnDay, StringComparison.Ordinal);
Expand Down
14 changes: 10 additions & 4 deletions Darling/Darling.Tests/DarlingPgAutovacuumVerdictLiveTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,18 @@ so the figure the verdict came from is visible. */
undersized call discloses its page scope (the get_pg_database_stats pattern). */
Assert.Equal(3, payload.GetProperty("past_threshold_count").GetInt32());
Assert.Equal(1, payload.GetProperty("growing_count").GetInt32());
Assert.False(payload.GetProperty("limit_reached").GetBoolean());

Assert.False(payload.GetProperty("truncated").GetBoolean());

/* #3653: the cut is OBSERVED off a limit + 1 fetch (#3594), so a limit of exactly the population
is complete and one below it is truncated with the page under its *_returned name. */
var whole = JsonDocument.Parse(
await DarlingMcpPgAutovacuumTools.GetPgAutovacuumHealth(dataSource, ServerName, 4, tables.Length)).RootElement;
Assert.False(whole.GetProperty("truncated").GetBoolean());
Assert.Equal(tables.Length, whole.GetProperty("tables_returned").GetInt32());
var truncated = JsonDocument.Parse(
await DarlingMcpPgAutovacuumTools.GetPgAutovacuumHealth(dataSource, ServerName, 4, 2)).RootElement;
Assert.Equal(2, truncated.GetProperty("table_count").GetInt32());
Assert.True(truncated.GetProperty("limit_reached").GetBoolean());
Assert.Equal(2, truncated.GetProperty("tables_returned").GetInt32());
Assert.True(truncated.GetProperty("truncated").GetBoolean());

bodySucceeded = true;
}
Expand Down
5 changes: 3 additions & 2 deletions Darling/Darling.Tests/McpFilterSemanticsLivePostgresTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,8 @@ NOT Healthy. */
Assert.Equal(1, ghost.GetProperty("collection_runs").GetInt64());
Assert.Equal("purged", ghost.GetProperty("data_state").GetString());
Assert.Equal("NoData", ghost.GetProperty("health_band").GetString());
Assert.Equal("No Data", ghost.GetProperty("overall_health").GetString());
/* #3653: one band, one spelling \u2014 overall_health carries the same token health_band does. */
Assert.Equal("NoData", ghost.GetProperty("overall_health").GetString());
Assert.Contains("PURGED", ghost.GetProperty("data_note").GetString(), StringComparison.Ordinal);
Assert.Contains(expectedHorizon.ToString("yyyy-MM-dd"), ghost.GetProperty("data_note").GetString(), StringComparison.Ordinal);

Expand Down Expand Up @@ -296,7 +297,7 @@ await DarlingMcpTestData.ExecAsync(connection, ct,
/* The single-day read of a past-horizon day is a DATA payload (the rows are there), not unavailable. */
var singlePast = JsonDocument.Parse(await DarlingMcpHealthTools.GetDailySummary(postgres, ServerName, ghostDay.ToString("yyyy-MM-dd"))).RootElement;
Assert.Equal("past_horizon", singlePast.GetProperty("data_state").GetString());
Assert.Equal("No Data", singlePast.GetProperty("overall_health").GetString());
Assert.Equal("NoData", singlePast.GetProperty("overall_health").GetString());
await DarlingMcpTestData.ExecAsync(connection, ct, $"DELETE FROM deadlocks WHERE server_id = {ServerId}");

/* The horizon is the store's EFFECTIVE retention: a fleet-wide override shortening one signal
Expand Down
20 changes: 16 additions & 4 deletions Darling/Darling.Tests/McpPageContractTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -799,10 +799,12 @@ public void PgServerConfig_NamesItsDenominators_InItsDescription()
/// the way <see cref="NoTool_ReadsAParameterAsItsAbsoluteValue_OnEitherSku"/> sweeps <c>Math.Abs</c>, with
/// the same population floor so a marker that stops matching cannot pass for free.
///
/// <para>What this does NOT sweep: <c>limit_reached = x.Count &gt;= limit</c>. That key says the page is as
/// long as the cap — true by construction — and its prose says "more MAY exist"; it is a weaker dialect,
/// not an inference, and whether it collapses into an observed <c>truncated</c> is the vocabulary lane's
/// call. <c>McpPayloadContractCensusTests</c> holds it in the truncation-key inventory.</para>
/// <para>What this does NOT sweep: <c>limit_reached = x.Count &gt;= limit</c>. That key said the page was as
/// long as the cap — true by construction — and its prose said "more MAY exist"; a weaker dialect, not an
/// inference, and the discriminator witness below keeps the non-match on record. The vocabulary lane
/// (#3653) then collapsed it: the four tools that spelled it moved onto <c>BoundPage</c> (they are in
/// <see cref="PgPagedToolsThroughTheHelper"/>) and <c>McpPayloadContractCensusTests.RetiredCutSpellings</c>
/// fails its return by name.</para>
/// </summary>
[Fact]
public void NoTool_InfersTruncationFromItsCap_OnEitherSku()
Expand Down Expand Up @@ -848,13 +850,23 @@ public void NoTool_InfersTruncationFromItsCap_OnEitherSku()
/// receive the <c>limit + 1</c> fetch and trim it; <c>DarlingMcpPgPlanToolsTests</c> executes both
/// boundaries against the builders, and <see cref="McpPageContractLivePostgresTests.PgPagedTools_FlipTruncatedExactlyAtTheBoundary_AgainstDevPostgres"/>
/// executes the deadlock and index reads against live Postgres.
///
/// <para>#3653 (the vocabulary lane) added the four tools that had spelled the cut <c>limit_reached =
/// x.Count &gt;= limit</c> — the inference this file's sweep deliberately did not flag because the key said
/// "as long as the cap", not "more exist" — now on the same helper with <c>truncated</c> and a
/// <c>*_returned</c> page count. <c>McpPayloadContractCensusTests</c> holds the vocabulary half (the
/// retired spelling cannot return); this roster holds the page contract.</para>
/// </summary>
public static readonly (Type Tools, string ToolName, string? Builder, string ReadName, string Sql)[] PgPagedToolsThroughTheHelper =
[
(typeof(DarlingMcpPgDeadlockTools), "get_pg_deadlocks", null, nameof(DarlingPgDeadlockReader.DeadlocksSql), DarlingPgDeadlockReader.DeadlocksSql),
(typeof(DarlingMcpPgPlanTools), "get_pg_plans", "BuildPlansJson", nameof(DarlingPgPlanCaptureReader.PgPlanCaptureSql), DarlingPgPlanCaptureReader.PgPlanCaptureSql),
(typeof(DarlingMcpPgPlanTools), "get_pg_plan_capture_readiness", "BuildReadinessJson", nameof(DarlingPgPlanCaptureReadinessReader.PgPlanCaptureReadinessSql), DarlingPgPlanCaptureReadinessReader.PgPlanCaptureReadinessSql),
(typeof(DarlingMcpPgIndexTools), "get_pg_index_bloat", null, nameof(DarlingPgIndexBloatReader.PgIndexBloatSql), DarlingPgIndexBloatReader.PgIndexBloatSql),
(typeof(DarlingMcpPgAutovacuumTools), "get_pg_autovacuum_health", null, nameof(DarlingPgAutovacuumReader.PgAutovacuumSql), DarlingPgAutovacuumReader.PgAutovacuumSql),
(typeof(DarlingMcpPgSessionStatesTools), "get_pg_session_states", null, nameof(DarlingPgSessionStatesReader.PgSessionStatesSql), DarlingPgSessionStatesReader.PgSessionStatesSql),
(typeof(DarlingMcpPgIndexUsageTools), "get_pg_index_usage", null, nameof(DarlingPgIndexUsageReader.PgIndexUsageSql), DarlingPgIndexUsageReader.PgIndexUsageSql),
(typeof(DarlingMcpPgTableBloatTools), "get_pg_table_bloat", null, nameof(DarlingPgTableBloatReader.PgTableBloatSql), DarlingPgTableBloatReader.PgTableBloatSql),
];

[Fact]
Expand Down
Loading
Loading