Cybersecurity-Projects/PROJECTS/intermediate/secrets-scanner/internal/hibp/client_test.go

204 lines
4.4 KiB
Go

// ©AngelaMos | 2026
// client_test.go
package hibp
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestSha1Hash(t *testing.T) {
t.Parallel()
got := sha1Hash("password")
assert.Equal(t,
"5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8",
got,
)
}
func TestClientCheckBreached(t *testing.T) {
t.Parallel()
hash := sha1Hash("password")
prefix := hash[:5]
suffix := hash[5:]
server := httptest.NewServer(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "/range/"+prefix, r.URL.Path)
w.WriteHeader(http.StatusOK)
fmt.Fprintf( //nolint:errcheck
w,
"%s:3861493\n",
suffix,
)
fmt.Fprintf( //nolint:errcheck
w,
"0000000000000000000000000000DEAD0:0\n",
)
}),
)
defer server.Close()
client := NewClient()
client.baseURL = server.URL + "/range/"
result, err := client.Check(context.Background(), "password")
require.NoError(t, err)
assert.True(t, result.Breached)
assert.Equal(t, 3861493, result.Count)
}
func TestClientCheckClean(t *testing.T) {
t.Parallel()
server := httptest.NewServer(
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, //nolint:errcheck
"0000000000000000000000000000AAAA0:5\n")
fmt.Fprintf( //nolint:errcheck
w,
"0000000000000000000000000000BBBB0:2\n",
)
}),
)
defer server.Close()
client := NewClient()
client.baseURL = server.URL + "/range/"
result, err := client.Check(
context.Background(), "unique_password_not_in_breach",
)
require.NoError(t, err)
assert.False(t, result.Breached)
assert.Equal(t, 0, result.Count)
}
func TestClientCachesResults(t *testing.T) {
t.Parallel()
callCount := 0
server := httptest.NewServer(
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
callCount++
hash := sha1Hash("cached_secret")
suffix := hash[5:]
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "%s:42\n", suffix) //nolint:errcheck
}),
)
defer server.Close()
client := NewClient()
client.baseURL = server.URL + "/range/"
result1, err := client.Check(
context.Background(), "cached_secret",
)
require.NoError(t, err)
assert.True(t, result1.Breached)
result2, err := client.Check(
context.Background(), "cached_secret",
)
require.NoError(t, err)
assert.True(t, result2.Breached)
assert.Equal(t, 42, result2.Count)
assert.Equal(t, 1, callCount)
}
func TestClientHandlesServerError(t *testing.T) {
t.Parallel()
server := httptest.NewServer(
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}),
)
defer server.Close()
client := NewClient()
client.baseURL = server.URL + "/range/"
_, err := client.Check(context.Background(), "test")
assert.Error(t, err)
}
func TestClientRetries429(t *testing.T) {
t.Parallel()
hash := sha1Hash("retryable")
suffix := hash[5:]
attempts := 0
server := httptest.NewServer(
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
attempts++
if attempts < 3 {
w.WriteHeader(http.StatusTooManyRequests)
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "%s:10\n", suffix) //nolint:errcheck
}),
)
defer server.Close()
client := NewClient()
client.baseURL = server.URL + "/range/"
result, err := client.Check(context.Background(), "retryable")
require.NoError(t, err)
assert.True(t, result.Breached)
assert.Equal(t, 10, result.Count)
assert.Equal(t, 3, attempts)
}
func TestClientRetries429Exhausted(t *testing.T) {
t.Parallel()
server := httptest.NewServer(
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusTooManyRequests)
}),
)
defer server.Close()
client := NewClient()
client.baseURL = server.URL + "/range/"
_, err := client.Check(context.Background(), "always429")
require.Error(t, err)
assert.Contains(t, err.Error(), "retries exhausted")
}
func TestClientContextCancellation(t *testing.T) {
t.Parallel()
server := httptest.NewServer(
http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprintf(w, "AAAA0:1\n") //nolint:errcheck
}),
)
defer server.Close()
client := NewClient()
client.baseURL = server.URL + "/range/"
ctx, cancel := context.WithCancel(context.Background())
cancel()
_, err := client.Check(ctx, "test")
assert.Error(t, err)
}