-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtotp_test.go
209 lines (174 loc) · 4.5 KB
/
totp_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package totp
import (
"testing"
"time"
)
func TestGenerateSecret(t *testing.T) {
// Test generating a secret
secret, err := GenerateSecret()
if err != nil {
t.Fatalf("Failed to generate secret: %v", err)
}
// Check secret length (base32 encoded, should be around 16 characters)
if len(secret) < 10 || len(secret) > 20 {
t.Errorf("Generated secret length is invalid: %d", len(secret))
}
}
func TestTOTP(t *testing.T) {
// Test scenarios
testCases := []struct {
name string
duration int
wantErr bool
}{
{"Standard 30-second interval", 30, false},
{"Custom 60-second interval", 60, false},
{"Invalid duration", -1, false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Generate a secret
secret, err := GenerateSecret()
if err != nil {
t.Fatalf("Failed to generate secret: %v", err)
}
// Generate TOTP
code, err := TOTP(secret, tc.duration)
if tc.wantErr && err == nil {
t.Error("Expected an error, but got none")
}
if err != nil && !tc.wantErr {
t.Errorf("Unexpected error: %v", err)
}
// Check code length
if len(code) != 6 {
t.Errorf("Invalid TOTP code length: %d", len(code))
}
})
}
}
// timeProvider is an interface to allow easier time mocking
type timeProvider interface {
Now() time.Time
}
// realTimeProvider uses the actual system time
type realTimeProvider struct{}
func (r realTimeProvider) Now() time.Time {
return time.Now()
}
// mockTimeProvider allows setting a fixed time for testing
type mockTimeProvider struct {
fixedTime time.Time
}
func (m mockTimeProvider) Now() time.Time {
return m.fixedTime
}
// Global variable to hold current time provider
var currentTimeProvider timeProvider = realTimeProvider{}
// SetTimeProvider allows setting a custom time provider (useful for testing)
func SetTimeProvider(provider timeProvider) {
currentTimeProvider = provider
}
// Modified functions to use the time provider
func nowFunc() time.Time {
return currentTimeProvider.Now()
}
// Updated test file
func TestValidate(t *testing.T) {
// Reset time provider after the test
defer SetTimeProvider(realTimeProvider{})
// Test validation scenarios
testCases := []struct {
name string
duration int
timeDiff time.Duration
expected bool
}{
{"Current time", 30, 0, true},
{"30 seconds before", 30, -30 * time.Second, true},
{"30 seconds after", 30, 30 * time.Second, true},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
// Create a fixed time and set it as the current time provider
fixedTime := time.Now().Add(tc.timeDiff)
SetTimeProvider(mockTimeProvider{fixedTime})
// Generate a secret
secret, err := GenerateSecret()
if err != nil {
t.Fatalf("Failed to generate secret: %v", err)
}
// Generate TOTP code
code, err := TOTP(secret, tc.duration)
if err != nil {
t.Fatalf("Failed to generate TOTP: %v", err)
}
// Validate the code
isValid := Validate(secret, tc.duration, code)
if isValid != tc.expected {
t.Errorf("Validation result unexpected. Got %v, want %v", isValid, tc.expected)
}
})
}
}
func TestInvalidSecret(t *testing.T) {
// Test with invalid secrets
invalidSecrets := []string{
"INVALID_SECRET",
"12345",
}
for _, secret := range invalidSecrets {
t.Run("Invalid Secret: "+secret, func(t *testing.T) {
// Try to generate TOTP with invalid secret
_, err := TOTP(secret, 30)
if err == nil {
t.Error("Expected an error with invalid secret, but got none")
}
// Try to validate with invalid secret
isValid := Validate(secret, 30, "123456")
if isValid {
t.Error("Validation should fail with invalid secret")
}
})
}
}
// Example of resetting to real time provider
func resetTimeProvider() {
SetTimeProvider(realTimeProvider{})
}
func BenchmarkGenerateSecret(b *testing.B) {
for i := 0; i < b.N; i++ {
_, _ = GenerateSecret()
}
}
func BenchmarkTOTP(b *testing.B) {
secret, _ := GenerateSecret()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = TOTP(secret, 30)
}
}
func BenchmarkValidate(b *testing.B) {
secret, _ := GenerateSecret()
code, _ := TOTP(secret, 30)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = Validate(secret, 30, code)
}
}
// Example of how to use the package
func ExampleTOTP() {
// Generate a secret
secret, err := GenerateSecret()
if err != nil {
panic(err)
}
// Generate a TOTP code
code, err := TOTP(secret, 30)
if err != nil {
panic(err)
}
// Validate the code
isValid := Validate(secret, 30, code)
println("Code is valid:", isValid)
}