package topdata import ( "reflect" "slices" "strings" "testing" ) func TestUnsupportedObjectKeysAreSorted(t *testing.T) { obj := map[string]any{ "row": map[string]any{}, "when_present": []any{}, "aaa": true, } got := unsupportedObjectKeys( obj, "row", "require_present", "any_present", "unless_present", ) want := []string{"aaa", "when_present"} if !slices.Equal(got, want) { t.Fatalf("unsupportedObjectKeys() = %v, want %v", got, want) } } func TestUnsupportedObjectKeysErrorNamesObjectAndContract(t *testing.T) { err := unsupportedObjectKeysError( "global injection 10", map[string]any{"row": map[string]any{}, "when_present": []any{}}, "row", "require_present", "any_present", "unless_present", ) if err == nil { t.Fatal("expected unsupported-property error") } for _, want := range []string{ "global injection 10", `"when_present"`, "row", "require_present", "any_present", "unless_present", } { if !strings.Contains(err.Error(), want) { t.Fatalf("error %q does not contain %q", err, want) } } } func TestParseGlobalConditionGroups(t *testing.T) { tests := []struct { name string injection map[string]any want globalConditionGroups wantError string }{ { name: "all groups", injection: map[string]any{ "require_present": []any{ map[string]any{"field": "FeatIndex", "id": "feat:required"}, }, "any_present": []any{ map[string]any{"field": "FeatIndex", "id": "feat:first"}, map[string]any{"field": "FeatIndex", "id": "feat:second"}, }, "unless_present": []any{ map[string]any{"field": "FeatIndex", "id": "feat:blocked"}, }, }, want: globalConditionGroups{ RequirePresent: []globalCondition{ {Field: "FeatIndex", ID: "feat:required"}, }, AnyPresent: []globalCondition{ {Field: "FeatIndex", ID: "feat:first"}, {Field: "FeatIndex", ID: "feat:second"}, }, UnlessPresent: []globalCondition{ {Field: "FeatIndex", ID: "feat:blocked"}, }, }, }, { name: "empty existing groups remain valid", injection: map[string]any{ "require_present": []any{}, "unless_present": []any{}, }, want: globalConditionGroups{ RequirePresent: []globalCondition{}, UnlessPresent: []globalCondition{}, }, }, { name: "any present must be array", injection: map[string]any{ "any_present": "feat:first", }, wantError: "any_present must be an array", }, { name: "any present must not be empty", injection: map[string]any{ "any_present": []any{}, }, wantError: "any_present must contain at least one condition", }, { name: "condition is closed", injection: map[string]any{ "require_present": []any{ map[string]any{ "field": "FeatIndex", "id": "feat:required", "typo": true, }, }, }, wantError: `require_present[0] contains unsupported key "typo"`, }, { name: "field is required", injection: map[string]any{ "require_present": []any{ map[string]any{"id": "feat:required"}, }, }, wantError: "require_present[0].field is required", }, { name: "id is required", injection: map[string]any{ "require_present": []any{ map[string]any{"field": "FeatIndex"}, }, }, wantError: "require_present[0].id is required", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got, err := parseGlobalConditionGroups(tc.injection) if tc.wantError != "" { if err == nil { t.Fatalf("parseGlobalConditionGroups() error = nil, want %q", tc.wantError) } if !strings.Contains(err.Error(), tc.wantError) { t.Fatalf("parseGlobalConditionGroups() error = %q, want contains %q", err, tc.wantError) } return } if err != nil { t.Fatalf("parseGlobalConditionGroups() error = %v", err) } if !reflect.DeepEqual(got, tc.want) { t.Fatalf("parseGlobalConditionGroups() = %#v, want %#v", got, tc.want) } }) } t.Run("absent groups remain valid", func(t *testing.T) { got, err := parseGlobalConditionGroups(map[string]any{}) if err != nil { t.Fatalf("parseGlobalConditionGroups() error = %v", err) } if !reflect.DeepEqual(got, globalConditionGroups{}) { t.Fatalf("parseGlobalConditionGroups() = %#v, want zero value", got) } }) } func TestGlobalConditionGroupsMatch(t *testing.T) { rowsWithoutBlocked := []map[string]any{ {"FeatIndex": map[string]any{"id": "feat:required"}}, {"FeatIndex": map[string]any{"id": "feat:second"}}, } rowsWithBlocked := append([]map[string]any(nil), rowsWithoutBlocked...) rowsWithBlocked = append(rowsWithBlocked, map[string]any{"FeatIndex": map[string]any{"id": "feat:blocked"}}) if !globalConditionGroupsMatch(globalConditionGroups{ RequirePresent: []globalCondition{ {Field: "FeatIndex", ID: "feat:required"}, }, AnyPresent: []globalCondition{ {Field: "FeatIndex", ID: "feat:first"}, {Field: "FeatIndex", ID: "feat:second"}, }, }, rowsWithoutBlocked) { t.Fatal("expected required + later any_present alternative to match") } if globalConditionGroupsMatch(globalConditionGroups{ AnyPresent: []globalCondition{ {Field: "FeatIndex", ID: "feat:missing"}, }, }, rowsWithoutBlocked) { t.Fatal("expected missing any_present alternatives to reject") } if globalConditionGroupsMatch(globalConditionGroups{ UnlessPresent: []globalCondition{ {Field: "FeatIndex", ID: "feat:blocked"}, }, }, rowsWithBlocked) { t.Fatal("expected unless_present match to reject") } }