Skip to content

Commit 0401209

Browse files
committed
test: assert delivery timestamp in unix second
1 parent e086bf1 commit 0401209

File tree

7 files changed

+53
-1
lines changed

7 files changed

+53
-1
lines changed

internal/destregistry/providers/destawskinesis/destawskinesis_publish_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ func (a *KinesisAsserter) AssertMessage(t testsuite.TestingT, msg testsuite.Mess
204204

205205
// Verify system metadata
206206
assert.NotEmpty(t, metadata["timestamp"], "timestamp should be present")
207+
testsuite.AssertTimestampIsUnixSeconds(t, metadata["timestamp"])
207208
assert.Equal(t, event.ID, metadata["event-id"], "event-id should match")
208209
assert.Equal(t, event.Topic, metadata["topic"], "topic should match")
209210

internal/destregistry/providers/destawss3/destawss3_publish_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ func (a *S3Asserter) AssertMessage(t testsuite.TestingT, msg testsuite.Message,
140140
// 2. Assert system metadata is present
141141
metadata := msg.Metadata
142142
assert.NotEmpty(t, metadata["timestamp"], "timestamp should be present")
143+
testsuite.AssertTimestampIsUnixSeconds(t, metadata["timestamp"])
143144
assert.Equal(t, event.ID, metadata["event-id"], "event-id should match")
144145
assert.Equal(t, event.Topic, metadata["topic"], "topic should match")
145146

internal/destregistry/providers/destawssqs/destawssqs_publish_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ func (a *SQSAsserter) AssertMessage(t testsuite.TestingT, msg testsuite.Message,
9494

9595
// Verify system metadata
9696
assert.NotEmpty(t, metadata["timestamp"], "timestamp should be present")
97+
testsuite.AssertTimestampIsUnixSeconds(t, metadata["timestamp"])
9798
assert.Equal(t, event.ID, metadata["event-id"], "event-id should match")
9899
assert.Equal(t, event.Topic, metadata["topic"], "topic should match")
99100

internal/destregistry/providers/destazureservicebus/destazureservicebus_publish_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ func (a *AzureServiceBusAsserter) AssertMessage(t testsuite.TestingT, msg testsu
119119

120120
// Verify system metadata
121121
assert.NotEmpty(t, metadata["timestamp"], "timestamp should be present")
122+
testsuite.AssertTimestampIsUnixSeconds(t, metadata["timestamp"])
122123
assert.Equal(t, event.ID, metadata["event-id"], "event-id should match")
123124
assert.Equal(t, event.Topic, metadata["topic"], "topic should match")
124125

internal/destregistry/providers/destrabbitmq/destrabbitmq_publish_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ func (a *RabbitMQAsserter) AssertMessage(t testsuite.TestingT, msg testsuite.Mes
147147
// Verify system metadata
148148
metadata := msg.Metadata
149149
assert.NotEmpty(t, metadata["timestamp"], "timestamp should be present")
150+
testsuite.AssertTimestampIsUnixSeconds(t, metadata["timestamp"])
150151
assert.Equal(t, event.ID, metadata["event-id"], "event-id should match")
151152
assert.Equal(t, event.Topic, metadata["topic"], "topic should match")
152153

internal/destregistry/providers/destwebhook/destwebhook_publish_test.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ func (a *WebhookAsserter) AssertMessage(t testsuite.TestingT, msg testsuite.Mess
9191
assert.Equal(t, "application/json", req.Header.Get("Content-Type"))
9292

9393
// Verify default headers
94-
assert.NotEmpty(t, req.Header.Get(a.headerPrefix+"timestamp"), "timestamp header should be present")
94+
timestampHeader := req.Header.Get(a.headerPrefix + "timestamp")
95+
assert.NotEmpty(t, timestampHeader, "timestamp header should be present")
96+
97+
// Verify timestamp is in Unix seconds (not milliseconds)
98+
testsuite.AssertTimestampIsUnixSeconds(t, timestampHeader)
99+
95100
assert.Equal(t, event.ID, req.Header.Get(a.headerPrefix+"event-id"), "event-id header should match")
96101
assert.Equal(t, event.Topic, req.Header.Get(a.headerPrefix+"topic"), "topic header should match")
97102

@@ -105,6 +110,13 @@ func (a *WebhookAsserter) AssertMessage(t testsuite.TestingT, msg testsuite.Mess
105110
signatureHeader := req.Header.Get(a.headerPrefix + "signature")
106111
assertSignatureFormat(t, signatureHeader, a.expectedSignatures)
107112

113+
// Verify timestamp in signature header matches the timestamp header
114+
signatureParts := strings.SplitN(signatureHeader, ",", 2)
115+
if len(signatureParts) >= 2 {
116+
signatureTimestampStr := strings.TrimPrefix(signatureParts[0], "t=")
117+
assert.Equal(t, timestampHeader, signatureTimestampStr, "timestamp in signature header should match timestamp header")
118+
}
119+
108120
// Verify each expected signature
109121
for _, secret := range a.secrets {
110122
assertValidSignature(t, secret, msg.Data, signatureHeader)

internal/destregistry/testing/publisher_suite.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import (
44
"context"
55
"encoding/json"
66
"errors"
7+
"strconv"
78
"sync"
89
"sync/atomic"
910
"time"
1011

1112
"github.com/hookdeck/outpost/internal/destregistry"
1213
"github.com/hookdeck/outpost/internal/models"
1314
"github.com/hookdeck/outpost/internal/util/testutil"
15+
"github.com/stretchr/testify/assert"
1416
"github.com/stretchr/testify/suite"
1517
)
1618

@@ -35,6 +37,39 @@ type TestingT interface {
3537
Helper()
3638
}
3739

40+
// AssertTimestampIsUnixSeconds verifies that a timestamp string is in Unix seconds format (not milliseconds).
41+
// It checks if the timestamp is within a reasonable range for Unix seconds (between year 2000 and 2100).
42+
func AssertTimestampIsUnixSeconds(t TestingT, timestampStr string, msgAndArgs ...interface{}) {
43+
t.Helper()
44+
45+
timestampInt, err := strconv.ParseInt(timestampStr, 10, 64)
46+
assert.NoError(t, err, "timestamp should be a valid integer")
47+
48+
// Check if timestamp is in a reasonable range for Unix seconds
49+
// Year 2000: ~946,684,800
50+
// Year 2100: ~4,102,444,800
51+
// Current time in seconds: ~1,700,000,000 (2023-2024)
52+
// Current time in millis: ~1,700,000,000,000
53+
54+
minUnixSeconds := int64(946684800) // Jan 1, 2000
55+
maxUnixSeconds := int64(4102444800) // Jan 1, 2100
56+
57+
if timestampInt < minUnixSeconds || timestampInt > maxUnixSeconds {
58+
// Likely milliseconds - check if dividing by 1000 gives a reasonable timestamp
59+
possibleSeconds := timestampInt / 1000
60+
if possibleSeconds >= minUnixSeconds && possibleSeconds <= maxUnixSeconds {
61+
assert.Fail(t, "timestamp appears to be in milliseconds, expected Unix seconds",
62+
"timestamp %d is likely in milliseconds (would be %s if converted to seconds), expected Unix seconds (around %s)",
63+
timestampInt,
64+
time.Unix(possibleSeconds, 0).Format(time.RFC3339),
65+
time.Now().Format(time.RFC3339))
66+
} else {
67+
assert.Fail(t, "timestamp is out of reasonable range",
68+
"timestamp %d is not within reasonable Unix seconds range (year 2000-2100)", timestampInt)
69+
}
70+
}
71+
}
72+
3873
// MessageConsumer is the interface that providers must implement
3974
type MessageConsumer interface {
4075
// Consume returns a channel that receives messages

0 commit comments

Comments
 (0)