Skip to content

Commit

Permalink
fix(changetracking): alter the functioning of milli and nanoseconds i…
Browse files Browse the repository at this point in the history
…n timestamps (#1151)
  • Loading branch information
pranav-new-relic authored Jun 6, 2024
1 parent f92f8fa commit 848f298
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 2 deletions.
21 changes: 21 additions & 0 deletions internal/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package utils
import (
"strconv"
"strings"
"time"

"github.com/newrelic/newrelic-client-go/v2/pkg/nrtime"
)

// IntArrayToString converts an array of integers
Expand All @@ -17,3 +20,21 @@ func IntArrayToString(integers []int) string {

return strings.Join(sArray, ",")
}

// a helper function that is used to populate a timestamp with non-zero milliseconds
// if the millisecond count has been found zero, usually when generated with time.Time()
// in Golang which does not have a nanoseconds field; which helps mutations such as those
// in changetracking, the API of which requires a timestamp with non-zero milliseconds.
func GetSafeTimestampWithMilliseconds(inputTimestamp nrtime.EpochMilliseconds) nrtime.EpochMilliseconds {
timestamp := time.Time(inputTimestamp)

// since time.Time in Go does not have a milliseconds field, which is why the implementation
// of unmarshaling time.Time into a Unix timestamp in the serialization package relies on
// nanoseconds to produce a value of milliseconds, we try employing a similar logic below

if timestamp.Nanosecond() < 100000000 {
timestamp = timestamp.Add(time.Nanosecond * 100000000)
}

return nrtime.EpochMilliseconds(timestamp)
}
77 changes: 77 additions & 0 deletions internal/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ package utils

import (
"testing"
"time"

"github.com/newrelic/newrelic-client-go/v2/pkg/nrtime"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestIntArrayToString(t *testing.T) {
Expand All @@ -25,3 +28,77 @@ func TestIntArrayToString(t *testing.T) {
result = IntArrayToString([]int{1, 2, 3, 4})
assert.Equal(t, "1,2,3,4", result)
}

func TestGetSafeTimestampWithMilliseconds_ZeroNanoseconds(t *testing.T) {
t.Parallel()

now := time.Now()
actualNanoseconds := 0
expectedNanoseconds := actualNanoseconds + 100000000

actualResult := GetSafeTimestampWithMilliseconds(nrtime.EpochMilliseconds(
time.Date(
now.Year(),
now.Month(),
now.Day(),
now.Hour()-3,
now.Minute()-30,
0,
actualNanoseconds,
time.Local,
),
))

expectedResult := nrtime.EpochMilliseconds(
time.Date(
now.Year(),
now.Month(),
now.Day(),
now.Hour()-3,
now.Minute()-30,
0,
expectedNanoseconds,
time.Local,
),
)
require.Equal(t, actualResult, expectedResult)
}

func TestGetSafeTimestampWithMilliseconds_NonZeroNanoseconds(t *testing.T) {
t.Parallel()

now := time.Now()
actualNanoseconds := 123000000

// equal, since actualNanoSeconds > 100000000
expectedNanoseconds := actualNanoseconds

actualResult := GetSafeTimestampWithMilliseconds(nrtime.EpochMilliseconds(
time.Date(
now.Year(),
now.Month(),
now.Day(),
now.Hour()-3,
now.Minute()-30,
0,
actualNanoseconds,
time.Local,
),
))

expectedResult := nrtime.EpochMilliseconds(
time.Date(
now.Year(),
now.Month(),
now.Day(),
now.Hour()-3,
now.Minute()-30,
0,
expectedNanoseconds,
time.Local,
),
)

require.Equal(t, actualResult, expectedResult)

}
14 changes: 13 additions & 1 deletion pkg/changetracking/changetracking_api.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

120 changes: 119 additions & 1 deletion pkg/changetracking/changetracking_api_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/json"
"fmt"
"log"
"regexp"
"testing"
"time"

Expand Down Expand Up @@ -66,7 +67,7 @@ func TestChangeTrackingCreateDeployment_CustomAttributes(t *testing.T) {
DeepLink: "newrelic-client-go",
DeploymentType: ChangeTrackingDeploymentTypeTypes.BASIC,
Description: "This is a test description",
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUID),
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUIDNew),
GroupId: "deployment",
Timestamp: nrtime.EpochMilliseconds(time.Now()),
User: "newrelic-go-client",
Expand Down Expand Up @@ -109,6 +110,123 @@ func TestChangeTrackingCreateDeployment_TimestampError(t *testing.T) {
require.Nil(t, res)
}

func TestChangeTrackingCreateDeployment_OlderThan24HoursTimestampError(t *testing.T) {
t.Parallel()
now := time.Now()

a := newIntegrationTestClient(t)

input := ChangeTrackingDeploymentInput{
Changelog: "test",
Commit: "12345a",
DeepLink: "newrelic-client-go",
DeploymentType: ChangeTrackingDeploymentTypeTypes.BASIC,
Description: "This is a test description",
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUID),
GroupId: "deployment",
Timestamp: nrtime.EpochMilliseconds(
time.Date(
now.Year(),
now.Month(),
now.Day()-2,
now.Hour()-3,
now.Minute()-30,
0,
0,
time.Local,
),
),
User: "newrelic-go-client",
Version: "0.0.1",
}

res, err := a.ChangeTrackingCreateDeployment(
ChangeTrackingDataHandlingRules{ValidationFlags: []ChangeTrackingValidationFlag{ChangeTrackingValidationFlagTypes.FAIL_ON_FIELD_LENGTH}},
input,
)
require.Error(t, err)
require.Regexp(t, regexp.MustCompile("not be more than 24 hours"), err.Error())
require.Nil(t, res)
}

func TestChangeTrackingCreateDeployment_TimestampZeroNanosecondsTest(t *testing.T) {
t.Parallel()

a := newIntegrationTestClient(t)
now := time.Now()

input := ChangeTrackingDeploymentInput{
Changelog: "test",
Commit: "12345a",
DeepLink: "newrelic-client-go",
DeploymentType: ChangeTrackingDeploymentTypeTypes.BASIC,
Description: "This is a test description",
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUIDNew),
GroupId: "deployment",
Timestamp: nrtime.EpochMilliseconds(
time.Date(
now.Year(),
now.Month(),
now.Day(),
now.Hour()-3,
now.Minute()-30,
0,
0,
time.Local,
),
),
User: "newrelic-go-client",
Version: "0.0.1",
}

res, err := a.ChangeTrackingCreateDeployment(
ChangeTrackingDataHandlingRules{ValidationFlags: []ChangeTrackingValidationFlag{ChangeTrackingValidationFlagTypes.FAIL_ON_FIELD_LENGTH}},
input,
)
require.NoError(t, err)
require.NotNil(t, res.EntityGUID)
require.Equal(t, res.EntityGUID, input.EntityGUID)
}

func TestChangeTrackingCreateDeployment_TimestampNonZeroNanosecondsTest(t *testing.T) {
t.Parallel()

a := newIntegrationTestClient(t)
now := time.Now()

input := ChangeTrackingDeploymentInput{
Changelog: "test",
Commit: "12345a",
DeepLink: "newrelic-client-go",
DeploymentType: ChangeTrackingDeploymentTypeTypes.BASIC,
Description: "This is a test description",
EntityGUID: common.EntityGUID(testhelpers.IntegrationTestApplicationEntityGUIDNew),
GroupId: "deployment",
Timestamp: nrtime.EpochMilliseconds(
time.Date(
now.Year(),
now.Month(),
now.Day(),
now.Hour()-6,
now.Minute()-30,
0,
231567,
time.Local,
),
),
User: "newrelic-go-client",
Version: "0.0.1",
}

res, err := a.ChangeTrackingCreateDeployment(
ChangeTrackingDataHandlingRules{ValidationFlags: []ChangeTrackingValidationFlag{ChangeTrackingValidationFlagTypes.FAIL_ON_FIELD_LENGTH}},
input,
)
require.NoError(t, err)
require.NotNil(t, res.EntityGUID)
require.Equal(t, res.EntityGUID, input.EntityGUID)
}

func newIntegrationTestClient(t *testing.T) Changetracking {
tc := testhelpers.NewIntegrationTestConfig(t)

Expand Down

0 comments on commit 848f298

Please sign in to comment.