package module import ( "encoding/json" "strings" "testing" ) type testStanza struct { Message string `json:"message"` Count int `json:"count"` } func TestSnapshotPreservesOutputOrderAndJSON(t *testing.T) { snapshot, err := NewSnapshot([]Output{ {ID: Metadata, StanzaName: "metadata", Value: testStanza{Message: "first", Count: 1}}, {ID: AlertDigest, StanzaName: "alert_digest", Value: testStanza{Message: "second", Count: 2}}, }) if err != nil { t.Fatalf("NewSnapshot() error = %v", err) } if snapshot.SchemaVersion != SnapshotSchemaVersion { t.Fatalf("SchemaVersion = %q, want %q", snapshot.SchemaVersion, SnapshotSchemaVersion) } if snapshot.Outputs[0].ID != Metadata || snapshot.Outputs[1].ID != AlertDigest { t.Fatalf("Outputs order = %#v, want input order", snapshot.Outputs) } data, err := json.Marshal(snapshot) if err != nil { t.Fatalf("Marshal() error = %v", err) } got := string(data) want := `{"schemaVersion":"weatherreporter.modules.v1","outputs":[{"id":"metadata","stanzaName":"metadata","value":{"message":"first","count":1}},{"id":"alert_digest","stanzaName":"alert_digest","value":{"message":"second","count":2}}]}` if got != want { t.Fatalf("json = %s, want %s", got, want) } } func TestSnapshotRejectsDuplicateOutputs(t *testing.T) { _, err := NewSnapshot([]Output{ {ID: Metadata, StanzaName: "metadata", Value: struct{}{}}, {ID: Metadata, StanzaName: "other_metadata", Value: struct{}{}}, }) if err == nil || !strings.Contains(err.Error(), `duplicate module output "metadata"`) { t.Fatalf("duplicate module error = %v, want duplicate module output", err) } _, err = NewSnapshot([]Output{ {ID: Metadata, StanzaName: "metadata", Value: struct{}{}}, {ID: CurrentConditions, StanzaName: "metadata", Value: struct{}{}}, }) if err == nil || !strings.Contains(err.Error(), `duplicate stanza name "metadata"`) { t.Fatalf("duplicate stanza error = %v, want duplicate stanza name", err) } } func TestStanzaValueDecodesTypedOutput(t *testing.T) { snapshot, err := NewSnapshot([]Output{ {ID: Metadata, StanzaName: "metadata", Value: testStanza{Message: "available", Count: 3}}, }) if err != nil { t.Fatalf("NewSnapshot() error = %v", err) } value, found, err := StanzaValue[testStanza](snapshot, "metadata") if err != nil { t.Fatalf("StanzaValue() error = %v", err) } if !found { t.Fatal("StanzaValue() found = false, want true") } if value.Message != "available" || value.Count != 3 { t.Fatalf("StanzaValue() = %#v, want decoded stanza", value) } _, found, err = StanzaValue[testStanza](snapshot, "missing") if err != nil { t.Fatalf("StanzaValue(missing) error = %v", err) } if found { t.Fatal("StanzaValue(missing) found = true, want false") } } func TestSPCConvectiveModuleContractsAreStable(t *testing.T) { if SPCConvectiveOutlooks != ID("spc_convective_outlooks") { t.Fatalf("SPCConvectiveOutlooks = %q, want stable source/module ID", SPCConvectiveOutlooks) } if SPCConvectiveDiscussion != ID("spc_convective_discussion") { t.Fatalf("SPCConvectiveDiscussion = %q, want stable discussion module ID", SPCConvectiveDiscussion) } if CollectedSPCConvectiveOutlooks != FactRequirement("collected.spc_convective_outlooks") { t.Fatalf("CollectedSPCConvectiveOutlooks = %q, want collected requirement", CollectedSPCConvectiveOutlooks) } if RequiresDerivedSPCConvectiveOutlooks != FactRequirement("derived.spc_convective_outlooks") { t.Fatalf("RequiresDerivedSPCConvectiveOutlooks = %q, want derived requirement", RequiresDerivedSPCConvectiveOutlooks) } _ = SPCConvectiveOutlooksOptions{} _ = SPCConvectiveDiscussionOptions{} } func TestDailyPlanningModuleContractIsStable(t *testing.T) { if DailyPlanning != ID("daily_planning") { t.Fatalf("DailyPlanning = %q, want stable module ID", DailyPlanning) } _ = DailyPlanningOptions{} }