All checks were successful
ci/woodpecker/push/build-image Pipeline was successful
219 lines
7.4 KiB
Go
219 lines
7.4 KiB
Go
package postgres
|
|
|
|
import (
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
fksinks "gitea.maximumdirect.net/ejr/feedkit/sinks"
|
|
)
|
|
|
|
func TestWeatherPostgresSchemaShape(t *testing.T) {
|
|
s := PostgresSchema()
|
|
if s.MapEvent == nil {
|
|
t.Fatalf("PostgresSchema().MapEvent is nil")
|
|
}
|
|
|
|
wantTables := map[string]bool{
|
|
tableObservations: true,
|
|
tableObservationPresentWeather: true,
|
|
tableForecasts: true,
|
|
tableForecastPeriods: true,
|
|
tableForecastDiscussions: true,
|
|
tableForecastDiscussionKeyMessages: true,
|
|
tableWeatherStoryRuns: true,
|
|
tableWeatherStories: true,
|
|
tableAlertRuns: true,
|
|
tableAlerts: true,
|
|
tableAlertReferences: true,
|
|
tableOutlookRuns: true,
|
|
tableOutlooks: true,
|
|
tableOutlookDiscussions: true,
|
|
}
|
|
|
|
if len(s.Tables) != len(wantTables) {
|
|
t.Fatalf("PostgresSchema().Tables len = %d, want %d", len(s.Tables), len(wantTables))
|
|
}
|
|
|
|
seenIndexes := map[string]bool{}
|
|
for _, tbl := range s.Tables {
|
|
if !wantTables[tbl.Name] {
|
|
t.Fatalf("unexpected table %q in schema", tbl.Name)
|
|
}
|
|
if tbl.PruneColumn == "" {
|
|
t.Fatalf("table %q missing prune column", tbl.Name)
|
|
}
|
|
for _, idx := range tbl.Indexes {
|
|
if seenIndexes[idx.Name] {
|
|
t.Fatalf("duplicate index name %q", idx.Name)
|
|
}
|
|
seenIndexes[idx.Name] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWeatherPostgresSchemaIncludesOutlookTables(t *testing.T) {
|
|
runColumns := columnsForTable(t, tableOutlookRuns)
|
|
for _, col := range []string{"event_id", "event_kind", "event_source", "event_schema", "event_emitted_at", "event_effective_at", "location_id", "location_name", "latitude", "longitude", "as_of", "issued_at", "outlook_count", "discussion_count"} {
|
|
if !runColumns[col] {
|
|
t.Fatalf("%s missing %s column", tableOutlookRuns, col)
|
|
}
|
|
}
|
|
assertTablePrimaryKey(t, tableOutlookRuns, []string{"event_id"})
|
|
assertTableIndex(t, tableOutlookRuns, "idx_wf_outlook_run_location_as_of", []string{"location_id", "as_of"})
|
|
assertTableIndex(t, tableOutlookRuns, "idx_wf_outlook_run_as_of", []string{"as_of"})
|
|
|
|
outlookColumns := columnsForTable(t, tableOutlooks)
|
|
for _, col := range []string{"run_event_id", "outlook_index", "as_of", "outlook_id", "provider", "product", "day", "outlook_type", "label", "label_text", "severity_rank", "valid_from", "valid_to", "issued_at", "expires_at", "forecaster", "source_url", "image_url", "contains_location", "geometry_json"} {
|
|
if !outlookColumns[col] {
|
|
t.Fatalf("%s missing %s column", tableOutlooks, col)
|
|
}
|
|
}
|
|
for _, col := range []string{"headline", "summary", "discussion"} {
|
|
if outlookColumns[col] {
|
|
t.Fatalf("%s still includes legacy %s column", tableOutlooks, col)
|
|
}
|
|
}
|
|
assertTablePrimaryKey(t, tableOutlooks, []string{"run_event_id", "outlook_index"})
|
|
assertTableIndex(t, tableOutlooks, "idx_wf_outlooks_contains_valid", []string{"contains_location", "valid_from", "valid_to"})
|
|
assertTableIndex(t, tableOutlooks, "idx_wf_outlooks_day_type_label", []string{"day", "outlook_type", "label"})
|
|
assertTableIndex(t, tableOutlooks, "idx_wf_outlooks_valid", []string{"valid_from", "valid_to"})
|
|
|
|
discussionColumns := columnsForTable(t, tableOutlookDiscussions)
|
|
for _, col := range []string{"run_event_id", "discussion_index", "as_of", "day", "headline", "summary", "discussion", "updated_at"} {
|
|
if !discussionColumns[col] {
|
|
t.Fatalf("%s missing %s column", tableOutlookDiscussions, col)
|
|
}
|
|
}
|
|
assertTablePrimaryKey(t, tableOutlookDiscussions, []string{"run_event_id", "discussion_index"})
|
|
assertTablePruneColumn(t, tableOutlookDiscussions, "as_of")
|
|
assertTableIndex(t, tableOutlookDiscussions, "idx_wf_outlook_discussions_day_as_of", []string{"day", "as_of"})
|
|
assertTableUniqueIndex(t, tableOutlookDiscussions, "idx_wf_outlook_discussions_run_day", []string{"run_event_id", "day"})
|
|
}
|
|
|
|
func TestWeatherPostgresSchemaIncludesAlertEndsColumn(t *testing.T) {
|
|
alertColumns := columnsForTable(t, tableAlerts)
|
|
for _, col := range []string{"run_event_id", "alert_index", "as_of", "alert_id", "onset", "ends", "expires"} {
|
|
if !alertColumns[col] {
|
|
t.Fatalf("%s missing %s column", tableAlerts, col)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWeatherPostgresSchemaIncludesWeatherStoryColumns(t *testing.T) {
|
|
runColumns := columnsForTable(t, tableWeatherStoryRuns)
|
|
if !runColumns["as_of"] {
|
|
t.Fatalf("%s missing as_of column", tableWeatherStoryRuns)
|
|
}
|
|
if !runColumns["story_count"] {
|
|
t.Fatalf("%s missing story_count column", tableWeatherStoryRuns)
|
|
}
|
|
|
|
storyColumns := columnsForTable(t, tableWeatherStories)
|
|
for _, col := range []string{"start_time", "end_time", "updated_at", "title", "description", "alt_text", "priority", "story_order", "download_url"} {
|
|
if !storyColumns[col] {
|
|
t.Fatalf("%s missing %s column", tableWeatherStories, col)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWeatherPostgresSchemaParentTablesStartWithEnvelopeColumns(t *testing.T) {
|
|
for _, table := range []string{
|
|
tableObservations,
|
|
tableForecasts,
|
|
tableForecastDiscussions,
|
|
tableWeatherStoryRuns,
|
|
tableAlertRuns,
|
|
tableOutlookRuns,
|
|
} {
|
|
t.Run(table, func(t *testing.T) {
|
|
columns := orderedColumnsForTable(t, table)
|
|
want := parentEnvelopeColumns()
|
|
if len(columns) < len(want) {
|
|
t.Fatalf("%s has %d columns, want at least %d", table, len(columns), len(want))
|
|
}
|
|
if !reflect.DeepEqual(columns[:len(want)], want) {
|
|
t.Fatalf("%s envelope prefix = %#v, want %#v", table, columns[:len(want)], want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func assertTablePrimaryKey(t *testing.T, table string, want []string) {
|
|
t.Helper()
|
|
tbl := tableByName(t, table)
|
|
if strings.Join(tbl.PrimaryKey, ",") != strings.Join(want, ",") {
|
|
t.Fatalf("%s primary key = %#v, want %#v", table, tbl.PrimaryKey, want)
|
|
}
|
|
}
|
|
|
|
func assertTablePruneColumn(t *testing.T, table string, want string) {
|
|
t.Helper()
|
|
tbl := tableByName(t, table)
|
|
if tbl.PruneColumn != want {
|
|
t.Fatalf("%s prune column = %q, want %q", table, tbl.PruneColumn, want)
|
|
}
|
|
}
|
|
|
|
func assertTableIndex(t *testing.T, table string, name string, want []string) {
|
|
t.Helper()
|
|
assertTableIndexWithUnique(t, table, name, want, false)
|
|
}
|
|
|
|
func assertTableUniqueIndex(t *testing.T, table string, name string, want []string) {
|
|
t.Helper()
|
|
assertTableIndexWithUnique(t, table, name, want, true)
|
|
}
|
|
|
|
func assertTableIndexWithUnique(t *testing.T, table string, name string, want []string, unique bool) {
|
|
t.Helper()
|
|
tbl := tableByName(t, table)
|
|
for _, idx := range tbl.Indexes {
|
|
if idx.Name == name {
|
|
if strings.Join(idx.Columns, ",") != strings.Join(want, ",") {
|
|
t.Fatalf("%s index %s columns = %#v, want %#v", table, name, idx.Columns, want)
|
|
}
|
|
if idx.Unique != unique {
|
|
t.Fatalf("%s index %s unique = %v, want %v", table, name, idx.Unique, unique)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("%s missing index %s", table, name)
|
|
}
|
|
|
|
func tableByName(t *testing.T, table string) fksinks.PostgresTable {
|
|
t.Helper()
|
|
for _, tbl := range PostgresSchema().Tables {
|
|
if tbl.Name == table {
|
|
return tbl
|
|
}
|
|
}
|
|
t.Fatalf("missing table %q", table)
|
|
return fksinks.PostgresTable{}
|
|
}
|
|
|
|
func orderedColumnsForTable(t *testing.T, table string) []fksinks.PostgresColumn {
|
|
t.Helper()
|
|
|
|
schema := PostgresSchema()
|
|
for _, tbl := range schema.Tables {
|
|
if tbl.Name == table {
|
|
return tbl.Columns
|
|
}
|
|
}
|
|
t.Fatalf("missing table %q", table)
|
|
return nil
|
|
}
|
|
|
|
func columnsForTable(t *testing.T, table string) map[string]bool {
|
|
t.Helper()
|
|
|
|
ordered := orderedColumnsForTable(t, table)
|
|
cols := make(map[string]bool, len(ordered))
|
|
for _, col := range ordered {
|
|
cols[col.Name] = true
|
|
}
|
|
return cols
|
|
}
|