39 lines
1.5 KiB
Go
39 lines
1.5 KiB
Go
package growth
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// ValidateCatalog validates the database-backed catalog after it has been
|
|
// mapped into the deterministic growth model. Production catalog contents are
|
|
// intentionally not embedded here; calendar_events is the sole source.
|
|
func ValidateCatalog(events []Event) error {
|
|
seen := make(map[string]struct{}, len(events))
|
|
for index, event := range events {
|
|
if strings.TrimSpace(event.ID) == "" || strings.TrimSpace(event.TitleFA) == "" || strings.TrimSpace(event.Family) == "" || strings.TrimSpace(event.Source) == "" || strings.TrimSpace(event.SourceVersion) == "" {
|
|
return fmt.Errorf("growth catalog event %d is incomplete", index)
|
|
}
|
|
if _, exists := seen[event.ID]; exists {
|
|
return fmt.Errorf("growth catalog contains duplicate id %q", event.ID)
|
|
}
|
|
seen[event.ID] = struct{}{}
|
|
if event.StartDate.IsZero() || event.EndDate.Before(event.StartDate) {
|
|
return fmt.Errorf("growth catalog event %q has invalid date range", event.ID)
|
|
}
|
|
if event.Kind != EventOfficial && event.Kind != EventCommercial {
|
|
return fmt.Errorf("growth catalog event %q has invalid kind", event.ID)
|
|
}
|
|
if len(normalizedSet(event.Tags)) == 0 {
|
|
return fmt.Errorf("growth catalog event %q lacks tags", event.ID)
|
|
}
|
|
if event.Universal && len(event.Categories) != 0 {
|
|
return fmt.Errorf("growth catalog universal event %q must not contain categories", event.ID)
|
|
}
|
|
if !event.Universal && len(event.Categories) == 0 {
|
|
return fmt.Errorf("growth catalog scoped event %q lacks categories", event.ID)
|
|
}
|
|
}
|
|
return nil
|
|
}
|