113 lines
4.8 KiB
Go
113 lines
4.8 KiB
Go
package generatedtext
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestValidateHourlyGeneratedTextEnforcesContentLimits(t *testing.T) {
|
|
input := func(summary, discussion, timing string) []byte {
|
|
return []byte(fmt.Sprintf(`{"summary":%q,"forecast_discussion":%q,"precipitation_timing":%q}`, summary, discussion, timing))
|
|
}
|
|
for _, test := range []struct {
|
|
name string
|
|
input []byte
|
|
want string
|
|
}{
|
|
{name: "summary at limit", input: input(strings.Repeat("s", maxGeneratedTextStringCharacters), "discussion", "")},
|
|
{name: "summary over limit", input: input(strings.Repeat("s", maxGeneratedTextStringCharacters+1), "discussion", ""), want: "summary exceeds"},
|
|
{name: "discussion at limit", input: input("summary", strings.Repeat("d", maxHourlyDiscussionCharacters), "")},
|
|
{name: "discussion over limit", input: input("summary", strings.Repeat("d", maxHourlyDiscussionCharacters+1), ""), want: "forecast discussion exceeds"},
|
|
{name: "total at limit", input: input(strings.Repeat("s", 4_000), strings.Repeat("d", 12_000), strings.Repeat("t", 4_000))},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
err := validateHourlyJSON(test.input)
|
|
if test.want == "" && err != nil {
|
|
t.Fatalf("validate() error = %v", err)
|
|
}
|
|
if test.want != "" && (err == nil || !strings.Contains(err.Error(), test.want)) {
|
|
t.Fatalf("validate() error = %v, want %q", err, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateDayStyleGeneratedTextEnforcesDiscussionLimits(t *testing.T) {
|
|
for _, test := range []struct {
|
|
name string
|
|
input string
|
|
want string
|
|
}{
|
|
{
|
|
name: "paragraph count at limit",
|
|
input: fmt.Sprintf(`{"summary":"summary","forecast_discussion":[%s],"precipitation_timing":""}`, strings.TrimSuffix(strings.Repeat(`"p",`, maxDayStyleDiscussionParagraphs), ",")),
|
|
},
|
|
{
|
|
name: "paragraph count over limit",
|
|
input: fmt.Sprintf(`{"summary":"summary","forecast_discussion":[%s],"precipitation_timing":""}`, strings.TrimSuffix(strings.Repeat(`"p",`, maxDayStyleDiscussionParagraphs+1), ",")),
|
|
want: "12-paragraph limit",
|
|
},
|
|
{
|
|
name: "paragraph at limit",
|
|
input: fmt.Sprintf(`{"summary":"summary","forecast_discussion":[%q],"precipitation_timing":""}`, strings.Repeat("p", maxDayStyleDiscussionParagraphChars)),
|
|
},
|
|
{
|
|
name: "paragraph over limit",
|
|
input: fmt.Sprintf(`{"summary":"summary","forecast_discussion":[%q],"precipitation_timing":""}`, strings.Repeat("p", maxDayStyleDiscussionParagraphChars+1)),
|
|
want: "paragraphs exceed the 4000-character limit",
|
|
},
|
|
{
|
|
name: "total at limit",
|
|
input: fmt.Sprintf(`{"summary":%q,"forecast_discussion":[%q,%q,%q,%q],"precipitation_timing":""}`,
|
|
strings.Repeat("s", 4_000), strings.Repeat("p", 4_000), strings.Repeat("p", 4_000), strings.Repeat("p", 4_000), strings.Repeat("p", 4_000)),
|
|
},
|
|
{
|
|
name: "total over limit",
|
|
input: fmt.Sprintf(`{"summary":%q,"forecast_discussion":[%q,%q,%q,%q,%q],"precipitation_timing":""}`,
|
|
strings.Repeat("s", 4_000), strings.Repeat("p", 4_000), strings.Repeat("p", 4_000), strings.Repeat("p", 4_000), strings.Repeat("p", 4_000), strings.Repeat("p", 4_000)),
|
|
want: "exceeds the 20000-character limit",
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
err := validateDailyJSON([]byte(test.input))
|
|
if test.want == "" && err != nil {
|
|
t.Fatalf("validate() error = %v", err)
|
|
}
|
|
if test.want != "" && (err == nil || !strings.Contains(err.Error(), test.want)) {
|
|
t.Fatalf("validate() error = %v, want %q", err, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateGeneratedTextRejectsOversizedRawOutputWithoutLeakingContent(t *testing.T) {
|
|
valid := []byte(`{"summary":"summary","forecast_discussion":"discussion","precipitation_timing":""}`)
|
|
atLimit := append([]byte(strings.Repeat(" ", MaxGeneratedTextBytes-len(valid))), valid...)
|
|
if err := validateHourlyJSON(atLimit); err != nil {
|
|
t.Fatalf("validate() at raw limit error = %v", err)
|
|
}
|
|
|
|
marker := "provider-controlled-marker"
|
|
unknownPrefix := `{"summary":"summary","forecast_discussion":"discussion","precipitation_timing":"","`
|
|
unknownSuffix := `":"value"}`
|
|
unknownKey := marker + strings.Repeat("x", MaxGeneratedTextBytes-len(unknownPrefix)-len(unknownSuffix)-len(marker))
|
|
unknownField := []byte(unknownPrefix + unknownKey + unknownSuffix)
|
|
err := validateHourlyJSON(unknownField)
|
|
if err == nil || !strings.Contains(err.Error(), "unsupported field") {
|
|
t.Fatalf("validate() error = %v, want unsupported field error", err)
|
|
}
|
|
if len(err.Error()) > 128 || strings.Contains(err.Error(), marker) {
|
|
t.Fatalf("validate() leaked unknown field content: %q", err)
|
|
}
|
|
|
|
input := append(atLimit, []byte(marker)...)
|
|
err = validateHourlyJSON(input)
|
|
if err == nil || !strings.Contains(err.Error(), "65536-byte limit") {
|
|
t.Fatalf("validate() error = %v, want raw size error", err)
|
|
}
|
|
if len(err.Error()) > 128 || strings.Contains(err.Error(), marker) {
|
|
t.Fatalf("validate() leaked oversized provider content: %q", err)
|
|
}
|
|
}
|