<!-- Add the related story/sub-task/bug number, like Resolves #123, or remove if NA --> **Related issue:** Resolves #42508 Renames abm/apple_business_manager to ab/apple_business in API and fleetctl. Uses existing renameto logic with a slight twist: added "inline" option to handle cases particularly where a single object tree has renames in multiple versions so that we don't break backwards compatibiility since the default behavior when you have multi-level renames is a new/old split at the top level # Checklist for submitter If some of the following don't apply, delete the relevant line. - [x] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. - [x] Input data is properly validated, `SELECT *` is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters. - [x] Timeouts are implemented and retries are limited to avoid infinite loops - [x] If paths of existing endpoints are modified without backwards compatibility, checked the frontend/CLI for any necessary changes ## Testing - [x] Added/updated automated tests - [x] Where appropriate, [automated tests simulate multiple hosts and test for host isolation](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/reference/patterns-backend.md#unit-testing) (updates to one hosts's records do not affect another) - [x] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Canonical Apple Business (AB) API endpoints and CLI: /api/v1/fleet/ab_tokens, /api/v1/fleet/mdm/apple/ab_public_key, plus new fleetctl get mdm-ab and fleetctl generate mdm-ab * New GitOps/config key: mdm.apple_business * Admin UI updated to show Apple Business tokens with fleet-based associations and updated labels * **Deprecations** * Legacy ABM endpoints, CLI aliases, and config keys remain supported but emit deprecation warnings pointing to the new AB equivalents <!-- end of auto-generated comment: release notes by coderabbit.ai -->
220 lines
5.9 KiB
Go
220 lines
5.9 KiB
Go
package endpointer
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type extractAliasRulesSuite struct {
|
|
suite.Suite
|
|
}
|
|
|
|
func TestExtractAliasRules(t *testing.T) {
|
|
suite.Run(t, new(extractAliasRulesSuite))
|
|
}
|
|
|
|
// SetupTest runs before every Test* method, clearing the global cache.
|
|
func (s *extractAliasRulesSuite) SetupTest() {
|
|
aliasRulesCache.Range(func(key, _ any) bool {
|
|
aliasRulesCache.Delete(key)
|
|
return true
|
|
})
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestNilInput() {
|
|
rules := ExtractAliasRules(nil)
|
|
require.Nil(s.T(), rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestNonStructInput() {
|
|
rules := ExtractAliasRules("hello")
|
|
require.Nil(s.T(), rules)
|
|
|
|
rules = ExtractAliasRules(42)
|
|
require.Nil(s.T(), rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestStructWithNoRenametoTags() {
|
|
type plain struct {
|
|
Name string `json:"name"`
|
|
Age int `json:"age"`
|
|
}
|
|
rules := ExtractAliasRules(plain{})
|
|
require.Empty(s.T(), rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestSingleRenametoTag() {
|
|
type singleAlias struct {
|
|
TeamID uint `json:"team_id" renameto:"group_id"`
|
|
}
|
|
rules := ExtractAliasRules(singleAlias{})
|
|
require.Equal(s.T(), []AliasRule{{OldKey: "team_id", NewKey: "group_id"}}, rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestRenametoInlineOption() {
|
|
type inlineAlias struct {
|
|
Tokens []string `json:"abm_tokens" renameto:"ab_tokens,inline"`
|
|
TeamID uint `json:"team_id" renameto:"fleet_id"`
|
|
}
|
|
rules := ExtractAliasRules(inlineAlias{})
|
|
s.Require().Equal([]AliasRule{
|
|
{OldKey: "abm_tokens", NewKey: "ab_tokens", Inline: true},
|
|
{OldKey: "team_id", NewKey: "fleet_id"},
|
|
}, rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestMultipleRenametoTags() {
|
|
type multiAlias struct {
|
|
TeamID uint `json:"team_id" renameto:"group_id"`
|
|
TeamName string `json:"team_name" renameto:"group_name"`
|
|
Other string `json:"other"`
|
|
}
|
|
rules := ExtractAliasRules(multiAlias{})
|
|
require.Equal(s.T(), []AliasRule{
|
|
{OldKey: "team_id", NewKey: "group_id"},
|
|
{OldKey: "team_name", NewKey: "group_name"},
|
|
}, rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestJsonTagWithOmitempty() {
|
|
type omitemptyAlias struct {
|
|
TeamID uint `json:"team_id,omitempty" renameto:"group_id"`
|
|
}
|
|
rules := ExtractAliasRules(omitemptyAlias{})
|
|
require.Equal(s.T(), []AliasRule{{OldKey: "team_id", NewKey: "group_id"}}, rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestJsonTagDashIsSkipped() {
|
|
type dashJSON struct {
|
|
Secret string `json:"-" renameto:"new_secret"`
|
|
}
|
|
rules := ExtractAliasRules(dashJSON{})
|
|
require.Empty(s.T(), rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestNoJsonTagWithRenametoIsSkipped() {
|
|
type noJSON struct {
|
|
Field string `renameto:"new_field"`
|
|
}
|
|
rules := ExtractAliasRules(noJSON{})
|
|
require.Empty(s.T(), rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestEmptyRenametoIsSkipped() {
|
|
type emptyRenameTo struct {
|
|
Field string `json:"field" renameto:""`
|
|
}
|
|
rules := ExtractAliasRules(emptyRenameTo{})
|
|
require.Empty(s.T(), rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestNestedStruct() {
|
|
type inner struct {
|
|
InnerField string `json:"inner_field" renameto:"new_inner"`
|
|
}
|
|
type outer struct {
|
|
OuterField string `json:"outer_field" renameto:"new_outer"`
|
|
Nested inner
|
|
}
|
|
rules := ExtractAliasRules(outer{})
|
|
require.Equal(s.T(), []AliasRule{
|
|
{OldKey: "outer_field", NewKey: "new_outer"},
|
|
{OldKey: "inner_field", NewKey: "new_inner"},
|
|
}, rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestDeeplyNestedStructs() {
|
|
type level2 struct {
|
|
Deep string `json:"deep" renameto:"new_deep"`
|
|
}
|
|
type level1 struct {
|
|
Mid string `json:"mid" renameto:"new_mid"`
|
|
Nested level2
|
|
}
|
|
type level0 struct {
|
|
Top string `json:"top" renameto:"new_top"`
|
|
Nested level1
|
|
}
|
|
rules := ExtractAliasRules(level0{})
|
|
require.Equal(s.T(), []AliasRule{
|
|
{OldKey: "top", NewKey: "new_top"},
|
|
{OldKey: "mid", NewKey: "new_mid"},
|
|
{OldKey: "deep", NewKey: "new_deep"},
|
|
}, rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestDeduplicationAcrossNestedStructs() {
|
|
type shared struct {
|
|
TeamID uint `json:"team_id" renameto:"group_id"`
|
|
}
|
|
type parent struct {
|
|
TeamID uint `json:"team_id" renameto:"group_id"`
|
|
Child shared
|
|
}
|
|
rules := ExtractAliasRules(parent{})
|
|
// The same OldKey→NewKey pair should appear only once.
|
|
require.Equal(s.T(), []AliasRule{{OldKey: "team_id", NewKey: "group_id"}}, rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestPointerToStructInput() {
|
|
type ptrInput struct {
|
|
Field string `json:"field" renameto:"new_field"`
|
|
}
|
|
rules := ExtractAliasRules(&ptrInput{})
|
|
require.Equal(s.T(), []AliasRule{{OldKey: "field", NewKey: "new_field"}}, rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestNestedThroughPointerField() {
|
|
type pointed struct {
|
|
Inner string `json:"inner" renameto:"new_inner"`
|
|
}
|
|
type wrapper struct {
|
|
Ptr *pointed
|
|
}
|
|
rules := ExtractAliasRules(wrapper{})
|
|
require.Equal(s.T(), []AliasRule{{OldKey: "inner", NewKey: "new_inner"}}, rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestNestedThroughSliceField() {
|
|
type elem struct {
|
|
Val string `json:"val" renameto:"new_val"`
|
|
}
|
|
type sliceWrapper struct {
|
|
Items []elem
|
|
}
|
|
rules := ExtractAliasRules(sliceWrapper{})
|
|
require.Equal(s.T(), []AliasRule{{OldKey: "val", NewKey: "new_val"}}, rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestNestedThroughMapField() {
|
|
type mapVal struct {
|
|
Key string `json:"key" renameto:"new_key"`
|
|
}
|
|
type mapWrapper struct {
|
|
Data map[string]mapVal
|
|
}
|
|
rules := ExtractAliasRules(mapWrapper{})
|
|
require.Equal(s.T(), []AliasRule{{OldKey: "key", NewKey: "new_key"}}, rules)
|
|
}
|
|
|
|
func (s *extractAliasRulesSuite) TestDeduplicationSameStructViaMultiplePaths() {
|
|
type common struct {
|
|
ID string `json:"old_id" renameto:"new_id"`
|
|
}
|
|
type branch1 struct {
|
|
C common
|
|
}
|
|
type branch2 struct {
|
|
C common
|
|
}
|
|
type root struct {
|
|
B1 branch1
|
|
B2 branch2
|
|
}
|
|
rules := ExtractAliasRules(root{})
|
|
// common's rule should appear only once even though reachable via two paths.
|
|
require.Equal(s.T(), []AliasRule{{OldKey: "old_id", NewKey: "new_id"}}, rules)
|
|
}
|