package app import ( "bytes" "reflect" "testing" "gitea.maximumdirect.net/eric/weatherreporter/internal/collect" "gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata" ) func TestPrepareReportBuildsImmutableDeterministicInputs(t *testing.T) { cfg := generationConfig() bundle := generationBundle(t) resolved, err := ResolveGenerate(GenerateRequest{ Config: cfg, Report: ReportDaily, Date: generationTime("2026-05-29T12:00:00-05:00"), Now: generationTime("2026-05-29T08:30:00-05:00"), }, generationTime("2026-05-29T08:30:00-05:00")) if err != nil { t.Fatalf("ResolveGenerate() error = %v", err) } request := prepareReportRequest{Config: cfg, Resolved: resolved, Collection: collect.Result{Bundle: &bundle}} prepared, err := prepareReport(request) if err != nil { t.Fatalf("prepareReport() error = %v", err) } repeated, err := prepareReport(request) if err != nil { t.Fatalf("second prepareReport() error = %v", err) } if len(prepared.dataPackage) == 0 || !bytes.Equal(prepared.dataPackage, repeated.dataPackage) || !reflect.DeepEqual(prepared.briefingMetadata, repeated.briefingMetadata) { t.Fatalf("prepared package/metadata are not deterministic: %q/%#v", prepared.dataPackage, prepared.briefingMetadata) } originalDataPackage := append([]byte(nil), prepared.dataPackage...) originalMetadata := prepared.briefingMetadata originalWarnings := append([]weatherdata.SourceWarning(nil), prepared.sourceWarnings...) metadata, snapshot, reportFacts, err := prepared.renderInputs() if err != nil { t.Fatalf("renderInputs() error = %v", err) } metadata.SourceWarnings = append(metadata.SourceWarnings, weatherdata.SourceWarning{Source: "test", Message: "consumer mutation"}) snapshot.Outputs = nil reportFacts.Collected.Hourly.Periods[0].TextDescription = "consumer mutation" bundle.Hourly.Periods[0].TextDescription = "mutated after preparation" bundle.Warnings = append(bundle.Warnings, weatherdata.SourceWarning{Source: "test", Message: "mutated warning"}) if len(bundle.Sources) > 0 { if bundle.Sources[0].Query == nil { bundle.Sources[0].Query = map[string]string{} } bundle.Sources[0].Query["mutated"] = "true" } if !bytes.Equal(prepared.dataPackage, originalDataPackage) || !reflect.DeepEqual(prepared.briefingMetadata, originalMetadata) || !reflect.DeepEqual(prepared.sourceWarnings, originalWarnings) { t.Fatalf("prepared values changed after caller mutation: %#v", prepared) } if prepared.reportFacts.Collected.Hourly.Periods[0].TextDescription == "mutated after preparation" { t.Fatal("prepared report facts retain caller-owned weather data") } if prepared.reportFacts.Collected.Hourly.Periods[0].TextDescription == "consumer mutation" || len(prepared.moduleSnapshot.Outputs) == 0 { t.Fatal("prepared report values retain consumer mutation") } }