Some checks failed
SonarQube / SonarQube (push) Failing after 5m28s
E2e test on main branch push / server and client test (map[name:admin port:8080]) (push) Failing after 3m33s
Server and lib tests / Library test (js) (push) Failing after 1m36s
Server and lib tests / Library test (vue) (push) Successful in 2m24s
Server and lib tests / Web client test (admin) (push) Successful in 2m52s
Server and lib tests / Web client test (compose) (push) Successful in 3m23s
Server and lib tests / Web client test (discovery) (push) Successful in 2m49s
Server and lib tests / Web client test (one) (push) Successful in 2m52s
Server and lib tests / Web client test (privacy) (push) Successful in 2m44s
Server and lib tests / Web client test (reporter) (push) Successful in 2m56s
Server and lib tests / Web client test (workflow) (push) Successful in 3m0s
Server and lib tests / Server tests (push) Failing after 4m55s
E2e test on main branch push / notify-failure (push) Failing after 9s
Server and lib tests / Send matrix testing notification (push) Successful in 2s
Mark stale issues and pull requests / stale (push) Successful in 40s
254 lines
5.5 KiB
Go
254 lines
5.5 KiB
Go
package revisions
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Note: These tests use json.Number because when reading revisions from DB,
|
|
// SetValue uses decoder.UseNumber() which decodes JSON numbers as json.Number
|
|
// (not uint64 or float64). This preserves precision for large uint64 values.
|
|
|
|
func Test_normalizeChangeValues(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
input []any
|
|
expected []any
|
|
}{
|
|
{
|
|
"empty",
|
|
[]any{},
|
|
nil,
|
|
},
|
|
{
|
|
"nil",
|
|
nil,
|
|
nil,
|
|
},
|
|
{
|
|
"json.Number to string",
|
|
[]any{json.Number("123456789012345")},
|
|
[]any{"123456789012345"},
|
|
},
|
|
{
|
|
"strings unchanged",
|
|
[]any{"test", "value"},
|
|
[]any{"test", "value"},
|
|
},
|
|
{
|
|
"booleans unchanged",
|
|
[]any{true, false},
|
|
[]any{true, false},
|
|
},
|
|
{
|
|
"mixed types",
|
|
[]any{json.Number("999"), "text", true, json.Number("12345678901234567890")},
|
|
[]any{"999", "text", true, "12345678901234567890"},
|
|
},
|
|
{
|
|
"regular numbers unchanged",
|
|
[]any{int64(123), float64(45.67)},
|
|
[]any{int64(123), float64(45.67)},
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
var (
|
|
req = require.New(t)
|
|
result = normalizeChangeValues(c.input)
|
|
)
|
|
req.Equal(c.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_Change_MarshalJSON(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
change *Change
|
|
expected string
|
|
}{
|
|
{
|
|
"strings",
|
|
&Change{
|
|
Key: "name",
|
|
Old: []any{"old name"},
|
|
New: []any{"new name"},
|
|
},
|
|
`{"key":"name","new":["new name"],"old":["old name"]}`,
|
|
},
|
|
{
|
|
"json.Number to strings",
|
|
&Change{
|
|
Key: "ownedBy",
|
|
Old: []any{json.Number("123456789012345")},
|
|
New: []any{json.Number("987654321098765")},
|
|
},
|
|
`{"key":"ownedBy","new":["987654321098765"],"old":["123456789012345"]}`,
|
|
},
|
|
{
|
|
"mixed types",
|
|
&Change{
|
|
Key: "status",
|
|
Old: []any{json.Number("1"), "active", true},
|
|
New: []any{json.Number("2"), "inactive", false},
|
|
},
|
|
`{"key":"status","new":["2","inactive",false],"old":["1","active",true]}`,
|
|
},
|
|
{
|
|
"empty old",
|
|
&Change{
|
|
Key: "newField",
|
|
Old: []any{},
|
|
New: []any{"value"},
|
|
},
|
|
`{"key":"newField","new":["value"]}`,
|
|
},
|
|
{
|
|
"empty new (deletion)",
|
|
&Change{
|
|
Key: "deletedField",
|
|
Old: []any{"value"},
|
|
New: []any{},
|
|
},
|
|
`{"key":"deletedField","old":["value"]}`,
|
|
},
|
|
{
|
|
"large uint64 values",
|
|
&Change{
|
|
Key: "createdBy",
|
|
Old: []any{json.Number("18446744073709551615")},
|
|
New: []any{json.Number("18446744073709551614")},
|
|
},
|
|
`{"key":"createdBy","new":["18446744073709551614"],"old":["18446744073709551615"]}`,
|
|
},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
var (
|
|
req = require.New(t)
|
|
result, err = c.change.MarshalJSON()
|
|
)
|
|
req.NoError(err)
|
|
req.JSONEq(c.expected, string(result))
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_Revision_SetValue_Delta_UseNumber(t *testing.T) {
|
|
t.Run("large uint64 preserved as json.Number", func(t *testing.T) {
|
|
var (
|
|
req = require.New(t)
|
|
r = &Revision{}
|
|
|
|
deltaJSON = []byte(`[
|
|
{
|
|
"key": "ownedBy",
|
|
"old": [123456789012345],
|
|
"new": [987654321098765]
|
|
},
|
|
{
|
|
"key": "name",
|
|
"old": ["old"],
|
|
"new": ["new"]
|
|
}
|
|
]`)
|
|
)
|
|
|
|
err := r.SetValue("delta", 0, deltaJSON)
|
|
req.NoError(err)
|
|
req.Len(r.Changes, 2)
|
|
|
|
req.Equal("ownedBy", r.Changes[0].Key)
|
|
req.Len(r.Changes[0].Old, 1)
|
|
req.Len(r.Changes[0].New, 1)
|
|
|
|
oldVal, ok := r.Changes[0].Old[0].(json.Number)
|
|
req.True(ok, "Old value should be json.Number")
|
|
req.Equal("123456789012345", oldVal.String())
|
|
|
|
newVal, ok := r.Changes[0].New[0].(json.Number)
|
|
req.True(ok, "New value should be json.Number")
|
|
req.Equal("987654321098765", newVal.String())
|
|
|
|
req.Equal("name", r.Changes[1].Key)
|
|
req.Equal("old", r.Changes[1].Old[0])
|
|
req.Equal("new", r.Changes[1].New[0])
|
|
})
|
|
|
|
t.Run("round-trip preserves precision", func(t *testing.T) {
|
|
var (
|
|
req = require.New(t)
|
|
original = &Change{
|
|
Key: "userId",
|
|
Old: []any{json.Number("18446744073709551615")},
|
|
New: []any{json.Number("18446744073709551614")},
|
|
}
|
|
)
|
|
|
|
marshaled, err := original.MarshalJSON()
|
|
req.NoError(err)
|
|
|
|
var intermediate map[string]any
|
|
err = json.Unmarshal(marshaled, &intermediate)
|
|
req.NoError(err)
|
|
req.Equal("userId", intermediate["key"])
|
|
|
|
oldSlice := intermediate["old"].([]any)
|
|
req.Equal("18446744073709551615", oldSlice[0])
|
|
|
|
newSlice := intermediate["new"].([]any)
|
|
req.Equal("18446744073709551614", newSlice[0])
|
|
})
|
|
}
|
|
|
|
func Test_Revision_MarshalJSON_Integration(t *testing.T) {
|
|
t.Run("full revision with uint64 strings", func(t *testing.T) {
|
|
var (
|
|
req = require.New(t)
|
|
r = &Revision{
|
|
ID: 1234567890123456789,
|
|
ResourceID: 9876543210987654321,
|
|
UserID: 1111111111111111111,
|
|
Revision: 5,
|
|
Operation: "update",
|
|
Comment: "Test revision",
|
|
Changes: []*Change{
|
|
{
|
|
Key: "ownedBy",
|
|
Old: []any{json.Number("123456789012345")},
|
|
New: []any{json.Number("987654321098765")},
|
|
},
|
|
},
|
|
}
|
|
)
|
|
|
|
data, err := json.Marshal(r)
|
|
req.NoError(err)
|
|
|
|
var result map[string]any
|
|
err = json.Unmarshal(data, &result)
|
|
req.NoError(err)
|
|
|
|
req.Equal("1234567890123456789", result["changeID"])
|
|
req.Equal("9876543210987654321", result["resourceID"])
|
|
req.Equal("1111111111111111111", result["userID"])
|
|
|
|
changes := result["changes"].([]any)
|
|
req.Len(changes, 1)
|
|
|
|
change := changes[0].(map[string]any)
|
|
req.Equal("ownedBy", change["key"])
|
|
|
|
old := change["old"].([]any)
|
|
req.Equal("123456789012345", old[0])
|
|
|
|
new := change["new"].([]any)
|
|
req.Equal("987654321098765", new[0])
|
|
})
|
|
}
|