Skip to content

Commit 0b4d526

Browse files
committed
split isTimestampOlder to its own file, and add robust tests
1 parent ba3af59 commit 0b4d526

File tree

6 files changed

+197
-101
lines changed

6 files changed

+197
-101
lines changed

__tests__/functions/commit-safety-checks.test.js

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import {
2-
commitSafetyChecks,
3-
isTimestampOlder
4-
} from '../../src/functions/commit-safety-checks'
1+
import {commitSafetyChecks} from '../../src/functions/commit-safety-checks'
52
import {COLORS} from '../../src/functions/colors'
63
import * as core from '@actions/core'
74

5+
jest.mock('../../src/functions/is-timestamp-older', () => ({
6+
isTimestampOlder: jest.fn()
7+
}))
8+
import {isTimestampOlder} from '../../src/functions/is-timestamp-older'
9+
810
const debugMock = jest.spyOn(core, 'debug').mockImplementation(() => {})
911
const infoMock = jest.spyOn(core, 'info').mockImplementation(() => {})
1012
const warningMock = jest.spyOn(core, 'warning').mockImplementation(() => {})
@@ -55,14 +57,12 @@ beforeEach(() => {
5557
})
5658

5759
test('checks a commit and finds that it is safe (date)', async () => {
60+
isTimestampOlder.mockReturnValue(false)
5861
expect(await commitSafetyChecks(context, data)).toStrictEqual({
5962
message: 'success',
6063
status: true,
6164
isVerified: false
6265
})
63-
expect(debugMock).toHaveBeenCalledWith(
64-
'2024-10-15T12:00:00Z is not older than 2024-10-15T11:00:00Z'
65-
)
6666
expect(debugMock).toHaveBeenCalledWith('isVerified: false')
6767
expect(debugMock).toHaveBeenCalledWith(
6868
`🔑 commit does not contain a verified signature but ${COLORS.highlight}commit signing is not required${COLORS.reset} - ${COLORS.success}OK${COLORS.reset}`
@@ -72,6 +72,7 @@ test('checks a commit and finds that it is safe (date)', async () => {
7272
})
7373

7474
test('checks a commit and finds that it is safe (date + verification)', async () => {
75+
isTimestampOlder.mockReturnValue(false)
7576
data.inputs.commit_verification = true
7677
data.commit.verification = {
7778
verified: true,
@@ -85,16 +86,14 @@ test('checks a commit and finds that it is safe (date + verification)', async ()
8586
status: true,
8687
isVerified: true
8788
})
88-
expect(debugMock).toHaveBeenCalledWith(
89-
'2024-10-15T12:00:00Z is not older than 2024-10-15T11:00:00Z'
90-
)
9189
expect(debugMock).toHaveBeenCalledWith('isVerified: true')
9290
expect(infoMock).toHaveBeenCalledWith(
9391
`🔑 commit signature is ${COLORS.success}valid${COLORS.reset}`
9492
)
9593
})
9694

9795
test('checks a commit and finds that it is not safe (date)', async () => {
96+
isTimestampOlder.mockReturnValue(true)
9897
data.commit.author.date = '2024-10-15T12:00:01Z'
9998

10099
expect(await commitSafetyChecks(context, data)).toStrictEqual({
@@ -103,13 +102,11 @@ test('checks a commit and finds that it is not safe (date)', async () => {
103102
status: false,
104103
isVerified: false
105104
})
106-
expect(debugMock).toHaveBeenCalledWith(
107-
'2024-10-15T12:00:00Z is older than 2024-10-15T12:00:01Z'
108-
)
109105
expect(debugMock).toHaveBeenCalledWith('isVerified: false')
110106
})
111107

112108
test('checks a commit and finds that it is not safe (verification)', async () => {
109+
isTimestampOlder.mockReturnValue(false)
113110
data.inputs.commit_verification = true
114111
data.commit.verification = {
115112
verified: false,
@@ -124,9 +121,6 @@ test('checks a commit and finds that it is not safe (verification)', async () =>
124121
status: false,
125122
isVerified: false
126123
})
127-
expect(debugMock).toHaveBeenCalledWith(
128-
'2024-10-15T12:00:00Z is not older than 2024-10-15T11:00:00Z'
129-
)
130124
expect(debugMock).toHaveBeenCalledWith('isVerified: false')
131125
expect(warningMock).toHaveBeenCalledWith(
132126
`🔑 commit signature is ${COLORS.error}invalid${COLORS.reset}`
@@ -136,6 +130,10 @@ test('checks a commit and finds that it is not safe (verification)', async () =>
136130
})
137131

138132
test('checks a commit and finds that it is not safe (verification time) even though it is verified - rejected due to timestamp', async () => {
133+
// First call: commit_created_at check (should be false), second call: verified_at check (should be true)
134+
isTimestampOlder
135+
.mockImplementationOnce(() => false)
136+
.mockImplementationOnce(() => true)
139137
data.inputs.commit_verification = true
140138
data.commit.verification = {
141139
verified: true,
@@ -150,9 +148,6 @@ test('checks a commit and finds that it is not safe (verification time) even tho
150148
status: false,
151149
isVerified: true
152150
})
153-
expect(debugMock).toHaveBeenCalledWith(
154-
'2024-10-15T12:00:00Z is not older than 2024-10-15T11:00:00Z'
155-
)
156151
expect(debugMock).toHaveBeenCalledWith('isVerified: true')
157152
expect(infoMock).toHaveBeenCalledWith(
158153
`🔑 commit signature is ${COLORS.success}valid${COLORS.reset}`
@@ -162,6 +157,12 @@ test('checks a commit and finds that it is not safe (verification time) even tho
162157
})
163158

164159
test('raises an error if the date format is invalid', async () => {
160+
// Simulate isTimestampOlder throwing
161+
isTimestampOlder.mockImplementation(() => {
162+
throw new Error(
163+
'Invalid date format. Please ensure the dates are valid UTC timestamps.'
164+
)
165+
})
165166
data.commit.author.date = '2024-10-15T12:00:uhoh'
166167
await expect(commitSafetyChecks(context, data)).rejects.toThrow(
167168
'Invalid date format. Please ensure the dates are valid UTC timestamps.'
@@ -184,6 +185,7 @@ test('throws if commit.author.date is missing', async () => {
184185
})
185186

186187
test('rejects a deployment if commit.verification.verified_at is null and commit_verification is true', async () => {
188+
isTimestampOlder.mockReturnValue(false)
187189
data.inputs.commit_verification = true
188190
data.commit.verification = {
189191
verified: true,
@@ -201,6 +203,7 @@ test('rejects a deployment if commit.verification.verified_at is null and commit
201203
})
202204

203205
test('rejects a deployment if commit.verification.verified_at is missing and commit_verification is true', async () => {
206+
isTimestampOlder.mockReturnValue(false)
204207
data.inputs.commit_verification = true
205208
data.commit.verification = {
206209
verified: true,
@@ -216,23 +219,8 @@ test('rejects a deployment if commit.verification.verified_at is missing and com
216219
})
217220
})
218221

219-
test('isTimestampOlder throws if timestampA is missing', () => {
220-
expect(() => isTimestampOlder(undefined, '2024-10-15T11:00:00Z')).toThrow(
221-
'One or both timestamps are missing or empty.'
222-
)
223-
})
224-
225-
test('isTimestampOlder throws if timestampB is missing', () => {
226-
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', undefined)).toThrow(
227-
'One or both timestamps are missing or empty.'
228-
)
229-
})
230-
231-
test('isTimestampOlder throws if both timestamps are invalid', () => {
232-
expect(() => isTimestampOlder('bad', 'bad')).toThrow(/Invalid date format/)
233-
})
234-
235222
test('isTimestampOlder covers else branch (not older)', async () => {
223+
isTimestampOlder.mockReturnValue(false)
236224
const context = {payload: {comment: {created_at: '2024-10-15T12:00:00Z'}}}
237225
const data = {
238226
sha: 'abc123',
@@ -249,7 +237,5 @@ test('isTimestampOlder covers else branch (not older)', async () => {
249237
inputs: {commit_verification: false}
250238
}
251239
await commitSafetyChecks(context, data)
252-
expect(debugMock).toHaveBeenCalledWith(
253-
'2024-10-15T12:00:00Z is not older than 2024-10-15T11:00:00Z'
254-
)
240+
expect(debugMock).toHaveBeenCalledWith('isVerified: false')
255241
})
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import * as core from '@actions/core'
2+
import {isTimestampOlder} from '../../src/functions/is-timestamp-older'
3+
4+
beforeEach(() => {
5+
jest.clearAllMocks()
6+
jest.spyOn(core, 'debug').mockImplementation(() => {})
7+
})
8+
9+
describe('isTimestampOlder', () => {
10+
test('throws if timestampA is missing', () => {
11+
expect(() => isTimestampOlder(undefined, '2024-10-15T11:00:00Z')).toThrow(
12+
'One or both timestamps are missing or empty.'
13+
)
14+
expect(() => isTimestampOlder(null, '2024-10-15T11:00:00Z')).toThrow(
15+
'One or both timestamps are missing or empty.'
16+
)
17+
expect(() => isTimestampOlder('', '2024-10-15T11:00:00Z')).toThrow(
18+
'One or both timestamps are missing or empty.'
19+
)
20+
})
21+
22+
test('throws if timestampB is missing', () => {
23+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', undefined)).toThrow(
24+
'One or both timestamps are missing or empty.'
25+
)
26+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', null)).toThrow(
27+
'One or both timestamps are missing or empty.'
28+
)
29+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', '')).toThrow(
30+
'One or both timestamps are missing or empty.'
31+
)
32+
})
33+
34+
test('throws if both timestamps are invalid', () => {
35+
expect(() => isTimestampOlder('bad', 'bad')).toThrow(
36+
/format YYYY-MM-DDTHH:MM:SSZ/
37+
)
38+
expect(() => isTimestampOlder('notadate', '2024-10-15T11:00:00Z')).toThrow(
39+
/format YYYY-MM-DDTHH:MM:SSZ/
40+
)
41+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', 'notadate')).toThrow(
42+
/format YYYY-MM-DDTHH:MM:SSZ/
43+
)
44+
expect(() => isTimestampOlder({}, '2024-10-15T11:00:00Z')).toThrow(
45+
/format YYYY-MM-DDTHH:MM:SSZ/
46+
)
47+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', {})).toThrow(
48+
/format YYYY-MM-DDTHH:MM:SSZ/
49+
)
50+
expect(() => isTimestampOlder([], '2024-10-15T11:00:00Z')).toThrow(
51+
/format YYYY-MM-DDTHH:MM:SSZ/
52+
)
53+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', [])).toThrow(
54+
/format YYYY-MM-DDTHH:MM:SSZ/
55+
)
56+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', 1)).toThrow(
57+
/format YYYY-MM-DDTHH:MM:SSZ/
58+
)
59+
expect(() => isTimestampOlder(1, '2024-10-15T11:00:00Z')).toThrow(
60+
/format YYYY-MM-DDTHH:MM:SSZ/
61+
)
62+
})
63+
64+
test('returns true if timestampA is older than timestampB', () => {
65+
expect(
66+
isTimestampOlder('2024-10-15T11:00:00Z', '2024-10-16T11:00:00Z')
67+
).toBe(true)
68+
expect(core.debug).toHaveBeenCalledWith(
69+
'2024-10-15T11:00:00Z is older than 2024-10-16T11:00:00Z'
70+
)
71+
})
72+
73+
test('returns false if timestampA is newer than timestampB', () => {
74+
expect(
75+
isTimestampOlder('2024-10-17T11:00:00Z', '2024-10-16T11:00:00Z')
76+
).toBe(false)
77+
expect(core.debug).toHaveBeenCalledWith(
78+
'2024-10-17T11:00:00Z is not older than 2024-10-16T11:00:00Z'
79+
)
80+
})
81+
82+
test('returns false if timestampA equals timestampB', () => {
83+
expect(
84+
isTimestampOlder('2024-10-16T11:00:00Z', '2024-10-16T11:00:00Z')
85+
).toBe(false)
86+
expect(core.debug).toHaveBeenCalledWith(
87+
'2024-10-16T11:00:00Z is not older than 2024-10-16T11:00:00Z'
88+
)
89+
})
90+
})

dist/index.js

Lines changed: 42 additions & 31 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/functions/commit-safety-checks.js

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as core from '@actions/core'
22
import {COLORS} from './colors'
3+
import {isTimestampOlder} from './is-timestamp-older'
34

45
// A helper method to ensure that the commit being used is safe for deployment
56
// These safety checks are supplemental to the checks found in `src/functions/prechecks.js`
@@ -88,34 +89,3 @@ export async function commitSafetyChecks(context, data) {
8889
isVerified: isVerified
8990
}
9091
}
91-
92-
// A helper method that checks if timestamp A is older than timestamp B
93-
// :param timestampA: The first timestamp to compare (String - format: "2024-10-21T19:10:24Z")
94-
// :param timestampB: The second timestamp to compare (String - format: "2024-10-21T19:10:24Z")
95-
// :returns: true if timestampA is older than timestampB, false otherwise
96-
export function isTimestampOlder(timestampA, timestampB) {
97-
// Defensive: handle null/undefined/empty
98-
if (!timestampA || !timestampB) {
99-
throw new Error('One or both timestamps are missing or empty.')
100-
}
101-
// Parse the date strings into Date objects
102-
const timestampADate = new Date(timestampA)
103-
const timestampBDate = new Date(timestampB)
104-
105-
// Check if the parsed dates are valid
106-
if (isNaN(timestampADate) || isNaN(timestampBDate)) {
107-
throw new Error(
108-
`Invalid date format. Please ensure the dates are valid UTC timestamps. Received: '${timestampA}', '${timestampB}'`
109-
)
110-
}
111-
112-
const result = timestampADate < timestampBDate
113-
114-
if (result) {
115-
core.debug(`${timestampA} is older than ${timestampB}`)
116-
} else {
117-
core.debug(`${timestampA} is not older than ${timestampB}`)
118-
}
119-
120-
return result
121-
}

0 commit comments

Comments
 (0)