Resolves #44970 (1/2). --- - [X] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. - [X] Added/updated automated tests - [X] QA'd all new/changed functionality manually <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * `fleetctl get fleets` / `get teams` now display software and setup experience from authoritative software endpoints. * Preserve literal setup_experience fields (avoid erroneous macos_setup renames) when applying and when transmitting JSON for software entries. * **Tests** * Added regression tests and test helpers to ensure software/setup_experience are sourced correctly and to prevent nil panics in related tests. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
596 lines
21 KiB
Go
596 lines
21 KiB
Go
package endpointer
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestJSONKeyRewriteReader_OldKeyPassThrough(t *testing.T) {
|
|
// Old (deprecated) key should pass through as-is and be tracked.
|
|
input := `{"team_id": 42, "name": "hello"}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(42), result["team_id"])
|
|
assert.Equal(t, "hello", result["name"])
|
|
assert.Nil(t, result["fleet_id"], "new key should not appear in output")
|
|
|
|
// Verify deprecated key was tracked.
|
|
assert.Equal(t, []string{"team_id"}, r.UsedDeprecatedKeys())
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_NewKeyRewritten(t *testing.T) {
|
|
// New key should be rewritten to old key for struct deserialization.
|
|
input := `{"fleet_id": 42, "name": "hello"}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(42), result["team_id"])
|
|
assert.Nil(t, result["fleet_id"], "new key should be rewritten to old")
|
|
assert.Empty(t, r.UsedDeprecatedKeys())
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_NoRewriteNeeded(t *testing.T) {
|
|
// Unrelated keys should pass through unchanged.
|
|
input := `{"other_field": 42, "name": "hello"}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(42), result["other_field"])
|
|
assert.Empty(t, r.UsedDeprecatedKeys())
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_AliasConflict(t *testing.T) {
|
|
input := `{"team_id": 42, "fleet_id": 99}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
_, err := io.ReadAll(r)
|
|
require.Error(t, err)
|
|
|
|
var ace *AliasConflictError
|
|
require.True(t, errors.As(err, &ace))
|
|
assert.Equal(t, "team_id", ace.Old)
|
|
assert.Equal(t, "fleet_id", ace.New)
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_AliasConflictNewThenOld(t *testing.T) {
|
|
// New key first, then deprecated key.
|
|
input := `{"fleet_id": 99, "team_id": 42}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
_, err := io.ReadAll(r)
|
|
require.Error(t, err)
|
|
|
|
var ace *AliasConflictError
|
|
require.True(t, errors.As(err, &ace))
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_NestedObjects(t *testing.T) {
|
|
input := `{"outer": {"team_id": 1}, "team_id": 2}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
// Old keys should pass through as-is.
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(2), result["team_id"])
|
|
inner := result["outer"].(map[string]any)
|
|
assert.Equal(t, float64(1), inner["team_id"])
|
|
|
|
assert.Contains(t, r.UsedDeprecatedKeys(), "team_id")
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_NestedNewKeys(t *testing.T) {
|
|
input := `{"outer": {"fleet_id": 1}, "fleet_id": 2}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
// New keys should be rewritten to old keys.
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(2), result["team_id"])
|
|
inner := result["outer"].(map[string]any)
|
|
assert.Equal(t, float64(1), inner["team_id"])
|
|
|
|
assert.Empty(t, r.UsedDeprecatedKeys())
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_NestedConflictDoesNotAffectOuter(t *testing.T) {
|
|
// Conflict in nested object should be detected, even though outer is fine.
|
|
input := `{"name": "ok", "inner": {"team_id": 1, "fleet_id": 2}}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
_, err := io.ReadAll(r)
|
|
require.Error(t, err)
|
|
|
|
var ace *AliasConflictError
|
|
require.True(t, errors.As(err, &ace))
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_NoConflictAcrossScopes(t *testing.T) {
|
|
// team_id in outer, fleet_id in inner — no conflict (different scopes).
|
|
input := `{"team_id": 1, "inner": {"fleet_id": 2}}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(1), result["team_id"])
|
|
inner := result["inner"].(map[string]any)
|
|
assert.Equal(t, float64(2), inner["team_id"]) // fleet_id rewritten to team_id
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_StringValuesNotRewritten(t *testing.T) {
|
|
// "team_id" as a string value (not a key) should NOT be rewritten.
|
|
input := `{"name": "team_id", "description": "the team_id field"}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
// String values should not be rewritten, only keys.
|
|
assert.Equal(t, "team_id", result["name"], "string value should not be rewritten")
|
|
assert.Equal(t, "the team_id field", result["description"])
|
|
// Make sure it didn't accidentally transform the team_id string value into a new fleet_id key.
|
|
assert.Empty(t, result["fleet_id"], "new key should not appear in output")
|
|
assert.Empty(t, r.UsedDeprecatedKeys())
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_ArrayValues(t *testing.T) {
|
|
input := `{"team_id": [1, 2, 3], "items": ["a", "b"]}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.NotNil(t, result["team_id"])
|
|
assert.Contains(t, r.UsedDeprecatedKeys(), "team_id")
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_ArrayOfObjects(t *testing.T) {
|
|
input := `{"items": [{"team_id": 1}, {"team_id": 2}]}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
items := result["items"].([]any)
|
|
for _, item := range items {
|
|
obj := item.(map[string]any)
|
|
assert.NotNil(t, obj["team_id"])
|
|
assert.Nil(t, obj["fleet_id"])
|
|
}
|
|
}
|
|
|
|
// TestJSONKeyRewriteReader_SoftwareSubtreeSkipsRules verifies that keys inside
|
|
// the `software` subtree are not subject to rename rules. The literal
|
|
// `setup_experience` install flag on SoftwarePackageSpec / TeamSpecAppStoreApp
|
|
// / MaintainedAppSpec items collides with the `macos_setup`↔`setup_experience`
|
|
// rename on the MDM section, and must be passed through untouched.
|
|
// Regression test for https://github.com/fleetdm/fleet/issues/44970.
|
|
func TestJSONKeyRewriteReader_SoftwareSubtreeSkipsRules(t *testing.T) {
|
|
input := `{
|
|
"setup_experience": {"enable_end_user_authentication": true},
|
|
"software": {
|
|
"packages": [
|
|
{"url": "http://foo", "setup_experience": true},
|
|
{"url": "http://bar", "setup_experience": false}
|
|
],
|
|
"app_store_apps": [
|
|
{"app_store_id": "1", "setup_experience": null}
|
|
],
|
|
"fleet_maintained_apps": [
|
|
{"slug": "foo", "setup_experience": true}
|
|
]
|
|
}
|
|
}`
|
|
rules := []AliasRule{{OldKey: "macos_setup", NewKey: "setup_experience"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
|
|
// Top-level `setup_experience` (the object) is rewritten to `macos_setup`.
|
|
assert.NotNil(t, result["macos_setup"], "top-level container key must be rewritten")
|
|
_, hasNewAtRoot := result["setup_experience"]
|
|
assert.False(t, hasNewAtRoot, "new key should have been rewritten at the root")
|
|
|
|
// Literal `setup_experience` flags inside software entries must NOT have
|
|
// been rewritten to `macos_setup`.
|
|
sw := result["software"].(map[string]any)
|
|
pkgs := sw["packages"].([]any)
|
|
assert.Equal(t, true, pkgs[0].(map[string]any)["setup_experience"])
|
|
assert.Equal(t, false, pkgs[1].(map[string]any)["setup_experience"])
|
|
_, hasMacOSSetupOnPkg := pkgs[0].(map[string]any)["macos_setup"]
|
|
assert.False(t, hasMacOSSetupOnPkg, "literal setup_experience inside software must not be rewritten")
|
|
|
|
apps := sw["app_store_apps"].([]any)
|
|
assert.Nil(t, apps[0].(map[string]any)["setup_experience"])
|
|
_, hasMacOSSetupOnApp := apps[0].(map[string]any)["macos_setup"]
|
|
assert.False(t, hasMacOSSetupOnApp, "null setup_experience inside software must not be rewritten")
|
|
|
|
fmas := sw["fleet_maintained_apps"].([]any)
|
|
assert.Equal(t, true, fmas[0].(map[string]any)["setup_experience"])
|
|
_, hasMacOSSetupOnFMA := fmas[0].(map[string]any)["macos_setup"]
|
|
assert.False(t, hasMacOSSetupOnFMA, "literal setup_experience on FMA must not be rewritten")
|
|
}
|
|
|
|
// TestRewriteOldToNewKeys_SoftwareSubtreeSkipsRules verifies the same software-
|
|
// scope skip in the reverse direction (old→new). A client posting a YAML with
|
|
// `setup_experience: true` on software items must not have those flags clobbered
|
|
// to `macos_setup` during client-side normalization.
|
|
func TestRewriteOldToNewKeys_SoftwareSubtreeSkipsRules(t *testing.T) {
|
|
input := `{
|
|
"macos_setup": {"enable_end_user_authentication": true},
|
|
"software": {
|
|
"packages": [{"url": "http://foo", "setup_experience": true}],
|
|
"app_store_apps": [{"app_store_id": "1", "setup_experience": true}],
|
|
"fleet_maintained_apps": [{"slug": "foo", "setup_experience": true}]
|
|
}
|
|
}`
|
|
rules := []AliasRule{{OldKey: "macos_setup", NewKey: "setup_experience"}}
|
|
|
|
out, err := RewriteOldToNewKeys([]byte(input), rules)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
|
|
// Top-level old `macos_setup` is rewritten to new `setup_experience`.
|
|
assert.NotNil(t, result["setup_experience"], "top-level old key must be rewritten to new")
|
|
_, hasOldAtRoot := result["macos_setup"]
|
|
assert.False(t, hasOldAtRoot, "old key should have been rewritten at the root")
|
|
|
|
// Literal `setup_experience` flags inside software entries must remain.
|
|
sw := result["software"].(map[string]any)
|
|
for _, key := range []string{"packages", "app_store_apps", "fleet_maintained_apps"} {
|
|
items := sw[key].([]any)
|
|
first := items[0].(map[string]any)
|
|
assert.Equal(t, true, first["setup_experience"], "literal flag on %s must be preserved", key)
|
|
_, hasOld := first["macos_setup"]
|
|
assert.False(t, hasOld, "literal setup_experience on %s must not be renamed to macos_setup", key)
|
|
}
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_MultipleRules(t *testing.T) {
|
|
input := `{"team_id": 1, "team_name": "Engineering"}`
|
|
rules := []AliasRule{
|
|
{OldKey: "team_id", NewKey: "fleet_id"},
|
|
{OldKey: "team_name", NewKey: "fleet_name"},
|
|
}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(1), result["team_id"])
|
|
assert.Equal(t, "Engineering", result["team_name"])
|
|
assert.Nil(t, result["fleet_id"])
|
|
assert.Nil(t, result["fleet_name"])
|
|
|
|
deprecated := r.UsedDeprecatedKeys()
|
|
assert.Len(t, deprecated, 2)
|
|
assert.Contains(t, deprecated, "team_id")
|
|
assert.Contains(t, deprecated, "team_name")
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_MultipleRulesNewKeys(t *testing.T) {
|
|
// New keys should be rewritten to old keys.
|
|
input := `{"fleet_id": 1, "fleet_name": "Engineering"}`
|
|
rules := []AliasRule{
|
|
{OldKey: "team_id", NewKey: "fleet_id"},
|
|
{OldKey: "team_name", NewKey: "fleet_name"},
|
|
}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(1), result["team_id"])
|
|
assert.Equal(t, "Engineering", result["team_name"])
|
|
assert.Nil(t, result["fleet_id"])
|
|
assert.Nil(t, result["fleet_name"])
|
|
|
|
assert.Empty(t, r.UsedDeprecatedKeys())
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_EmptyObject(t *testing.T) {
|
|
input := `{}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
assert.JSONEq(t, `{}`, string(out))
|
|
assert.Empty(t, r.UsedDeprecatedKeys())
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_NullValues(t *testing.T) {
|
|
input := `{"team_id": null}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Contains(t, result, "team_id")
|
|
assert.Nil(t, result["team_id"])
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_BooleanValues(t *testing.T) {
|
|
input := `{"team_id": true}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, true, result["team_id"])
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_NoRules(t *testing.T) {
|
|
input := `{"team_id": 42}`
|
|
var rules []AliasRule
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
// With no rules, output should be identical to input.
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(42), result["team_id"])
|
|
assert.Empty(t, r.UsedDeprecatedKeys())
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_LargePayload(t *testing.T) {
|
|
// Build a large JSON payload that exceeds the internal buffer size (4096 bytes).
|
|
var sb strings.Builder
|
|
sb.WriteString(`{"team_id": 1`)
|
|
for i := range 500 {
|
|
sb.WriteString(fmt.Sprintf(`, "field_%04d": "value"`, i))
|
|
}
|
|
sb.WriteString(`}`)
|
|
input := sb.String()
|
|
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(1), result["team_id"])
|
|
assert.Nil(t, result["fleet_id"])
|
|
assert.Contains(t, r.UsedDeprecatedKeys(), "team_id")
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_WithJSONDecoderOldKey(t *testing.T) {
|
|
// Simulate the real usage: json.NewDecoder reading from the rewriter
|
|
// with old (deprecated) key in the request. The struct uses old key names.
|
|
input := `{"team_id": 42, "name": "test"}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
rewriter := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
|
|
type request struct {
|
|
TeamID int `json:"team_id"` //nolint:apiparamcheck // rename handled centrally by spec.DeprecatedGitOpsKeyMappings
|
|
Name string `json:"name"`
|
|
}
|
|
var req request
|
|
err := json.NewDecoder(rewriter).Decode(&req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 42, req.TeamID)
|
|
assert.Equal(t, "test", req.Name)
|
|
assert.Contains(t, rewriter.UsedDeprecatedKeys(), "team_id")
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_WithJSONDecoderNewKey(t *testing.T) {
|
|
// Simulate the real usage: json.NewDecoder reading from the rewriter
|
|
// with new key in the request. Should be rewritten to old key.
|
|
input := `{"fleet_id": 42, "name": "test"}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
rewriter := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
|
|
type request struct {
|
|
TeamID int `json:"team_id"` //nolint:apiparamcheck // rename handled centrally by spec.DeprecatedGitOpsKeyMappings
|
|
Name string `json:"name"`
|
|
}
|
|
var req request
|
|
err := json.NewDecoder(rewriter).Decode(&req)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 42, req.TeamID)
|
|
assert.Equal(t, "test", req.Name)
|
|
assert.Empty(t, rewriter.UsedDeprecatedKeys())
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_AliasConflictWithJSONDecoder(t *testing.T) {
|
|
input := `{"team_id": 42, "fleet_id": 99}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
rewriter := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
|
|
type request struct {
|
|
TeamID int `json:"team_id"` //nolint:apiparamcheck // rename handled centrally by spec.DeprecatedGitOpsKeyMappings
|
|
}
|
|
var req request
|
|
err := json.NewDecoder(rewriter).Decode(&req)
|
|
require.Error(t, err)
|
|
|
|
var ace *AliasConflictError
|
|
require.True(t, errors.As(err, &ace))
|
|
assert.Equal(t, "team_id", ace.Old)
|
|
assert.Equal(t, "fleet_id", ace.New)
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_DeeplyNestedObjectsOldKeys(t *testing.T) {
|
|
input := `{"a": {"b": {"c": {"team_id": 99}}}}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
inner := result["a"].(map[string]any)["b"].(map[string]any)["c"].(map[string]any)
|
|
assert.Equal(t, float64(99), inner["team_id"])
|
|
assert.Contains(t, r.UsedDeprecatedKeys(), "team_id")
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_DeeplyNestedObjectsNewKeys(t *testing.T) {
|
|
input := `{"a": {"b": {"c": {"fleet_id": 99}}}}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
inner := result["a"].(map[string]any)["b"].(map[string]any)["c"].(map[string]any)
|
|
assert.Equal(t, float64(99), inner["team_id"])
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_TopLevelArrayOldKeys(t *testing.T) {
|
|
input := `[{"team_id": 1}, {"team_id": 2}]`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result []map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Len(t, result, 2)
|
|
assert.Equal(t, float64(1), result[0]["team_id"])
|
|
assert.Equal(t, float64(2), result[1]["team_id"])
|
|
assert.Contains(t, r.UsedDeprecatedKeys(), "team_id")
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_TopLevelArrayNewKeys(t *testing.T) {
|
|
input := `[{"fleet_id": 1}, {"fleet_id": 2}]`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result []map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Len(t, result, 2)
|
|
assert.Equal(t, float64(1), result[0]["team_id"])
|
|
assert.Equal(t, float64(2), result[1]["team_id"])
|
|
}
|
|
|
|
func TestJSONKeyRewriteReader_NestedObjectStringValue(t *testing.T) {
|
|
// Object as value with keys that need tracking.
|
|
input := `{"config": {"team_id": 5, "enabled": true}}`
|
|
rules := []AliasRule{{OldKey: "team_id", NewKey: "fleet_id"}}
|
|
|
|
r := NewJSONKeyRewriteReader(strings.NewReader(input), rules)
|
|
out, err := io.ReadAll(r)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
config := result["config"].(map[string]any)
|
|
assert.Equal(t, float64(5), config["team_id"])
|
|
assert.Equal(t, true, config["enabled"])
|
|
assert.Equal(t, []string{"team_id"}, r.UsedDeprecatedKeys())
|
|
}
|
|
|
|
func TestAliasConflictError_ErrorMessage(t *testing.T) {
|
|
err := &AliasConflictError{Old: "team_id", New: "fleet_id"}
|
|
assert.Contains(t, err.Error(), "team_id")
|
|
assert.Contains(t, err.Error(), "fleet_id")
|
|
}
|
|
|
|
func TestRewriteOldToNewKeys(t *testing.T) {
|
|
rules := []AliasRule{
|
|
{OldKey: "team_id", NewKey: "fleet_id"},
|
|
{OldKey: "team", NewKey: "fleet"},
|
|
{OldKey: "custom_settings", NewKey: "configuration_profiles"},
|
|
}
|
|
|
|
t.Run("rewrites old keys to new", func(t *testing.T) {
|
|
input := `{"team_id":42,"name":"hello","team":"engineering"}`
|
|
out, err := RewriteOldToNewKeys([]byte(input), rules)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(42), result["fleet_id"])
|
|
assert.Equal(t, "engineering", result["fleet"])
|
|
assert.Equal(t, "hello", result["name"])
|
|
assert.Nil(t, result["team_id"])
|
|
assert.Nil(t, result["team"])
|
|
})
|
|
|
|
t.Run("new keys pass through unchanged", func(t *testing.T) {
|
|
input := `{"fleet_id":42}`
|
|
out, err := RewriteOldToNewKeys([]byte(input), rules)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]any
|
|
require.NoError(t, json.Unmarshal(out, &result))
|
|
assert.Equal(t, float64(42), result["fleet_id"])
|
|
})
|
|
}
|