Skip to content

Commit 2372cf0

Browse files
authored
Merge pull request #392 from github/timestamp-older-improvements
Timestamp improvements
2 parents ba3af59 + 5b5dce9 commit 2372cf0

File tree

6 files changed

+285
-101
lines changed

6 files changed

+285
-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: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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+
jest.spyOn(core, 'error').mockImplementation(() => {})
8+
})
9+
10+
describe('isTimestampOlder', () => {
11+
test('throws if timestampA is missing', () => {
12+
expect(() => isTimestampOlder(undefined, '2024-10-15T11:00:00Z')).toThrow(
13+
'One or both timestamps are missing or empty.'
14+
)
15+
expect(() => isTimestampOlder(null, '2024-10-15T11:00:00Z')).toThrow(
16+
'One or both timestamps are missing or empty.'
17+
)
18+
expect(() => isTimestampOlder('', '2024-10-15T11:00:00Z')).toThrow(
19+
'One or both timestamps are missing or empty.'
20+
)
21+
})
22+
23+
test('throws if timestampB is missing', () => {
24+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', undefined)).toThrow(
25+
'One or both timestamps are missing or empty.'
26+
)
27+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', null)).toThrow(
28+
'One or both timestamps are missing or empty.'
29+
)
30+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', '')).toThrow(
31+
'One or both timestamps are missing or empty.'
32+
)
33+
})
34+
35+
test('throws if both timestamps are invalid', () => {
36+
expect(() => isTimestampOlder('bad', 'bad')).toThrow(
37+
/format YYYY-MM-DDTHH:MM:SSZ/
38+
)
39+
expect(() => isTimestampOlder('notadate', '2024-10-15T11:00:00Z')).toThrow(
40+
/format YYYY-MM-DDTHH:MM:SSZ/
41+
)
42+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', 'notadate')).toThrow(
43+
/format YYYY-MM-DDTHH:MM:SSZ/
44+
)
45+
expect(() => isTimestampOlder({}, '2024-10-15T11:00:00Z')).toThrow(
46+
/format YYYY-MM-DDTHH:MM:SSZ/
47+
)
48+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', {})).toThrow(
49+
/format YYYY-MM-DDTHH:MM:SSZ/
50+
)
51+
expect(() => isTimestampOlder([], '2024-10-15T11:00:00Z')).toThrow(
52+
/format YYYY-MM-DDTHH:MM:SSZ/
53+
)
54+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', [])).toThrow(
55+
/format YYYY-MM-DDTHH:MM:SSZ/
56+
)
57+
expect(() => isTimestampOlder('2024-10-15T11:00:00Z', 1)).toThrow(
58+
/format YYYY-MM-DDTHH:MM:SSZ/
59+
)
60+
expect(() => isTimestampOlder(1, '2024-10-15T11:00:00Z')).toThrow(
61+
/format YYYY-MM-DDTHH:MM:SSZ/
62+
)
63+
})
64+
65+
test('returns true if timestampA is older than timestampB', () => {
66+
expect(
67+
isTimestampOlder('2024-10-15T11:00:00Z', '2024-10-16T11:00:00Z')
68+
).toBe(true)
69+
expect(core.debug).toHaveBeenCalledWith(
70+
'2024-10-15T11:00:00Z is older than 2024-10-16T11:00:00Z'
71+
)
72+
})
73+
74+
test('returns false if timestampA is newer than timestampB', () => {
75+
expect(
76+
isTimestampOlder('2024-10-17T11:00:00Z', '2024-10-16T11:00:00Z')
77+
).toBe(false)
78+
expect(core.debug).toHaveBeenCalledWith(
79+
'2024-10-17T11:00:00Z is not older than 2024-10-16T11:00:00Z'
80+
)
81+
})
82+
83+
test('returns false if timestampA equals timestampB', () => {
84+
expect(
85+
isTimestampOlder('2024-10-16T11:00:00Z', '2024-10-16T11:00:00Z')
86+
).toBe(false)
87+
expect(core.debug).toHaveBeenCalledWith(
88+
'2024-10-16T11:00:00Z is not older than 2024-10-16T11:00:00Z'
89+
)
90+
})
91+
92+
test('accepts valid leap year date', () => {
93+
// Feb 29, 2024 is valid (leap year)
94+
expect(() =>
95+
isTimestampOlder('2024-02-29T12:00:00Z', '2024-10-15T11:00:00Z')
96+
).not.toThrow()
97+
expect(
98+
isTimestampOlder('2024-02-29T12:00:00Z', '2024-10-15T11:00:00Z')
99+
).toBe(true)
100+
expect(
101+
isTimestampOlder('2024-10-15T11:00:00Z', '2024-02-29T12:00:00Z')
102+
).toBe(false)
103+
})
104+
105+
test('throws an error on js silent date contructor corrections', () => {
106+
// Invalid date: 2024-02-30T12:00:00Z actually becomes 2024-03-01T12:00:00Z (gross)
107+
expect(() =>
108+
isTimestampOlder('2024-02-30T12:00:00Z', '2024-10-15T11:00:00Z')
109+
).toThrow(/Invalid date format/)
110+
expect(() =>
111+
isTimestampOlder('2024-10-15T11:00:00Z', '2024-02-30T12:00:00Z')
112+
).toThrow(/Invalid date format/)
113+
})
114+
})

0 commit comments

Comments
 (0)