Tighten NPC interaction validation and consistency

This commit is contained in:
2026-07-23 14:25:32 +00:00
parent 36e0512454
commit bfe25609a7
14 changed files with 426 additions and 278 deletions

View File

@@ -6,10 +6,13 @@ import (
"fmt"
"strconv"
"strings"
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
)
const (
MaxIssues = 20
MaxWarnings = 20
MaxDisplayedRunes = 128
MaxMessageBytes = 4096
)
@@ -37,6 +40,28 @@ func Aggregate(prefix string, issues []string) string {
return aggregateMessage(prefix, displayed, len(issues)-len(displayed))
}
// LimitWarnings returns at most MaxWarnings warnings, reserving the final
// position for a deterministic omission summary when truncation is required.
func LimitWarnings(warnings []contracts.Warning, scope, reasonCode string) []contracts.Warning {
if warnings == nil {
return nil
}
if len(warnings) <= MaxWarnings {
bounded := make([]contracts.Warning, len(warnings))
copy(bounded, warnings)
return bounded
}
displayed := MaxWarnings - 1
bounded := make([]contracts.Warning, displayed, MaxWarnings)
copy(bounded, warnings[:displayed])
bounded = append(bounded, contracts.Warning{
Scope: scope,
ReasonCode: reasonCode,
Message: fmt.Sprintf("%d additional warning(s) omitted", len(warnings)-displayed),
})
return bounded
}
func aggregateMessage(prefix string, issues []string, omitted int) string {
message := prefix + ": " + strings.Join(issues, ", ")
if omitted > 0 {

View File

@@ -2,9 +2,12 @@ package diagnostics
import (
"fmt"
"reflect"
"strings"
"testing"
"unicode/utf8"
"gitea.maximumdirect.net/eric/notarius/internal/framework/contracts"
)
func TestAggregateEnforcesByteBudgetAndReportsOmissions(t *testing.T) {
@@ -26,3 +29,25 @@ func TestAggregateEnforcesByteBudgetAndReportsOmissions(t *testing.T) {
t.Fatalf("Aggregate() = %q, want %q", message, wantOmitted)
}
}
func TestLimitWarningsBoundsOutputAndReportsOmissions(t *testing.T) {
warnings := make([]contracts.Warning, MaxWarnings+3)
for index := range warnings {
warnings[index] = contracts.Warning{ReasonCode: fmt.Sprintf("warning-%d", index)}
}
before := append([]contracts.Warning(nil), warnings...)
got := LimitWarnings(warnings, "records", "warnings_omitted")
if len(got) != MaxWarnings {
t.Fatalf("LimitWarnings() count = %d, want %d", len(got), MaxWarnings)
}
summary := got[len(got)-1]
wantOmitted := len(warnings) - (MaxWarnings - 1)
if summary.Scope != "records" || summary.ReasonCode != "warnings_omitted" ||
summary.Message != fmt.Sprintf("%d additional warning(s) omitted", wantOmitted) {
t.Fatalf("summary = %#v", summary)
}
if !reflect.DeepEqual(warnings, before) {
t.Fatal("LimitWarnings() mutated its input")
}
}