func Test_BlockAndUnblockUser(t *testing.T) {
	var code string
	var errCnt int32
	otpUser, len := initBlockedUserTest(t)
	clearErrcnt := []int32{len / 2, len + 2, len + 10, len + 2}
	clearErrCntIdx := 0
	hotp := testGetHotp(t)
	for i := int32(0); i < len*4; i++ {
		errCnt++
		if errCnt == clearErrcnt[clearErrCntIdx] {  
			clearErrCntIdx++
			code, _ = hotp.AtCount(hotp.Count)
			errCnt = 0
			otpUser.SetOtpUserBlockedState(false)
		} else if i > len*3 {  
			code, _ = hotp.AtCount(hotp.Count)
		} else {  
			code = wrongCode
		}
		ok, err := otpUser.VerifyOtpUserCode(code, HotpType)
		if ok {  
			hotp.Next()
		}
		locked, _ := otpUser.IsOtpUserBlocked()
		if err != nil && !locked {  
			t.Error("Test was not run, error:", err)
			t.FailNow()
		}
		if locked == true && errCnt <= len {
			t.Errorf("Test failed: User was locked before enougth itterations, try=%v, minimum tries=%v, idx=%v", errCnt, len, i)
		} else if locked == false && errCnt > len {
			t.Errorf("Test failed: User was not locked out after: %v wrong tries, the maximum number of tries before locked out are: %v, idx=%v", errCnt, len, i)
		}
	}
}
generate enougth errors to loc the user
Tests we are checking:
1. If the errCnt < throttle, the user is not blocked
2. If the errCnt > throttle, the user is blocked
3. If the user is not blocked, the right code is clearing the errCounter
4. If the user is blocked, the right code dosn't check therefor, it can't clear the errCounter
package otp
import (
	"fmt"
	"testing"
	"time"
	defs "github.com/ibm-security-innovation/libsecurity-go/defs"
)
const (
	wrongCode = "1234"
)
func testGenerateOtpUser(t *testing.T, thrTimeSec time.Duration) *UserInfoOtp {
	otpUser, err := NewOtpUser(BaseSecret, true, false, defaultThrottlingLen, thrTimeSec, manuelUnblockSec, defaultHotpWindowsSize, defaultTotpWindowsSizeSec, defaultStartCounter)
	if err != nil {
		t.Errorf("Test fail, can't generate otpUser, error: %v", err)
		t.FailNow()
	}
	return otpUser
}
func addDefaultOtpUserGetHotp(t *testing.T, throttleTimeSec time.Duration) (*UserInfoOtp, *Hotp) {
	otpUser := testGenerateOtpUser(t, throttleTimeSec)
	hotp := testGetHotp(t)
	return otpUser, hotp
}
func addDefaultOtpUserGetTotp(t *testing.T, throttleTimeSec time.Duration) (*UserInfoOtp, *Totp) {
	otpUser := testGenerateOtpUser(t, throttleTimeSec)
	totp := testGetTotp(t)
	return otpUser, totp
}
func Test_CheckErrorThrottling(t *testing.T) {
	throttleTimeSec := time.Duration(2)
	otpUser, hotp := addDefaultOtpUserGetHotp(t, throttleTimeSec)
	for i := 0; i < 10; i++ {
		refTime := time.Now()
		otpUser.setBlockedState(false)                                           
		factor := time.Duration(i+1) * throttleTimeSec                           
		ok, err := otpUser.verifyOtpUserCodeHelper(wrongCode, HotpType, factor)  
		if err != nil {
			t.Error("Test fail, err:", err)
		}
		throttleTimer, err := otpUser.getOtpUserThrottlingTimer(TotpType)
		if err != nil {
			t.Error("Test fail, err:", err)
		} else {
			expected := time.Now().Add(time.Duration(factor) * time.Second)
			if throttleTimer.After(expected) {
				fmt.Printf("Test failed, expected the throttle value to be lower than %v, but the return throttling value was %v. Time now is %v and factor is%v\n",
					expected, throttleTimer, time.Now(), factor)
				t.Errorf("Test failed, expected the throttle value to be lower than %v, but the returned throttling value was %v",
					expected, throttleTimer)
			}
			if i == 0 {  
				code, _ := hotp.AtCount(hotp.Count)
				codeOk, _ := otpUser.VerifyOtpUserCode(code, HotpType)  
				if codeOk {
					t.Error("Test fail, Time:", time.Now(), ", OTP shuld not be checked before:", throttleTimer)
				}
				codeOk, err = otpUser.verifyOtpUserCodeHelper(code, HotpType, factor)
				if debug {
					fmt.Println("Calculated code = ", code, ", match = ", ok)
				}
				if !codeOk || err != nil {
					t.Errorf("Test fail, expected to be throttle for: %vs, checked after %v, err: %v",
						throttleTimeSec, time.Now().Unix()-refTime.Unix(), err)
				}
				if codeOk {  
					hotp.Next()
				}
			}
		}
	}
}
func Test_CheckOtpWindowSizeLowLevel(t *testing.T) {
	windowSize := 5.0
	winSize := int64(windowSize)
	offsets := []float64{-1, 0, windowSize / 2, windowSize - 1, windowSize, windowSize + 1}
	otpUser, hotp := addDefaultOtpUserGetHotp(t, 0)
	for _, o := range offsets {
		offset := int64(o)
		code, _ := hotp.AtCount(hotp.Count + offset)
		found, newOffset, _ := otpUser.findHotpCodeMatch(code, int32(winSize))
		if (offset < 0 || offset >= winSize) && found {
			t.Error("Test fail, Code for offset ", offset, "was found, but the window size for code search is 0 to ", winSize)
		} else if (offset >= 0 && offset < winSize) && !found {
			t.Error("Test fail, Code for offset ", offset, "was not found, but the window size for code search is 0 to", winSize)
		} else if found && newOffset != int32(offset) {
			t.Error("Test fail, Code was found, but the calculated offset:", newOffset, "is not as expected:", offset)
		}
	}
}
type testOffset struct {
	internalCounterOffset int64
	codeOffset            int64
	expected              bool
}
func Test_CheckOtpCounterUpdated(t *testing.T) {
	otpUser, hotp := addDefaultOtpUserGetHotp(t, 0)
	otpUser.Throttle.CheckHotpWindow = maxHotpWindowSize
	otpUser.Throttle.Cliff = maxThrottlingCounter  
	tests := []testOffset{{0, maxHotpWindowSize + 1, false}, {0, maxHotpWindowSize, false},
		{0, maxHotpWindowSize - 1, true},                      
		{0, 1, false},                                         
		{0, maxHotpWindowSize, true},                          
		{0, maxHotpWindowSize - 1, false},                     
		{maxHotpWindowSize + 3, maxHotpWindowSize + 2, true},  
		{maxHotpWindowSize, maxHotpWindowSize - 1, true},      
	}
	for i, test := range tests {
		code, _ := hotp.AtCount(hotp.Count + test.codeOffset)
		found, err := otpUser.VerifyOtpUserCode(code, HotpType)
		if err != nil {
			t.Error("Test fail, error:", err)
		}
		if found != test.expected {
			t.Error("Test ", i, "fail, provider counter is:", otpUser.BaseHotp.Count,
				"counter used for code calculatations:", hotp.Count+test.codeOffset,
				"expected that the code will be match:", test.expected, "but code match:", found)
		}
		if found {
			hotp.Count += test.internalCounterOffset  
		}
	}
}
func Test_CheckOtpTotpWindow(t *testing.T) {
	otpUser, totp := addDefaultOtpUserGetTotp(t, 0)
	otpUser.Throttle.Cliff = maxThrottlingCounter  
	offsets := []int{-62, -25, -2, 0}
	windowsOffsets := []int{-30}
	sign := []int{1, -1}
	for _, s := range sign {
		for _, w := range windowsOffsets {
			w = w * s
			for i, offset := range offsets {
				offset = offset * s
				otpUser.Throttle.lastTotpCode = ""  
				otpUser.Throttle.CheckTotpWindowSec = time.Duration(w)
				testTime := time.Now().Add(time.Duration(offset) * time.Second)
				code, _ := totp.AtTime(testTime)
				found, err := otpUser.VerifyOtpUserCode(code, TotpType)
				if debug {
					fmt.Println("Test:", i, "Window offset:", w, "Offset:", offset, ", Code was match:", found)
				}
				if err != nil {
					t.Error("Test fail, error:", err)
				}
				if found && offset*s < w*s {
					t.Error("Test ", i, "fail, Totp code match, but the provider time:", time.Now(),
						"is not in the used time for code calculations:", testTime,
						"window offset:", w, "seconds")
				}
			}
		}
	}
}
func Test_CheckAutomaticUnblockUser(t *testing.T) {
	offsetsSec := []time.Duration{-2, 0}
	otpUser, hotp := addDefaultOtpUserGetHotp(t, 0)
	otpUser.Throttle.AutoUnblockSec = defaultUnblockSec
	for i := int32(0); i < otpUser.Throttle.Cliff+1; i++ {
		_, err := otpUser.VerifyOtpUserCode(wrongCode, HotpType)
		if err != nil && i < otpUser.Throttle.Cliff {
			t.Error("Test fail, err:", err)
		}
	}
	blocked, _ := otpUser.IsOtpUserBlocked()
	if !blocked {
		t.Error("Test fail, User must be blocked after", otpUser.Throttle.Cliff, "wrong tries")
		t.FailNow()
	}
	expected := time.Now().Add(time.Duration(otpUser.Throttle.AutoUnblockSec) * time.Second)
	throttleAutoUnblockedTimer := otpUser.getAutoUnBlockedTimer()
	if throttleAutoUnblockedTimer.After(expected) {
		t.Errorf("Test fail, expected the throttle value to be till: %v, but the return throttling value was %v",
			expected, throttleAutoUnblockedTimer)
	} else {
		code, _ := hotp.AtCount(hotp.Count)
		for _, o := range offsetsSec {
			otpUser.verifyOtpUserCodeHelper(code, HotpType, otpUser.Throttle.AutoUnblockSec+o)
			blocked, _ := otpUser.isOtpUserBlockedHelper(otpUser.Throttle.AutoUnblockSec + o)
			if !blocked && o < 0 {
				t.Error("Test fail, User must not be automatically unblocked before", otpUser.Throttle.AutoUnblockSec,
					"seconds, but it was unblocked after", otpUser.Throttle.AutoUnblockSec+o, "seconds")
			} else if blocked && o >= 0 {
				t.Error("Test fail, User must be automatically unblocked after", otpUser.Throttle.AutoUnblockSec,
					"seconds, time pass since blocked", otpUser.Throttle.AutoUnblockSec+o, "seconds")
			}
		}
	}
}
func Test_ChangeUserBlocked(t *testing.T) {
	blockedStates := []bool{true, false, true}
	otpUser := testGenerateOtpUser(t, 0)
	for _, b := range blockedStates {
		err := otpUser.SetOtpUserBlockedState(b)
		if err != nil {
			t.Errorf("Test fail, can't change OTP user blocked state to %v, error: %v", b, err)
		} else {
			state, _ := otpUser.IsOtpUserBlocked()
			if state != b {
				t.Errorf("Test fail, Otp user lockedOut state set to %v but read %v", b, state)
			}
		}
	}
}
func initBlockedUserTest(t *testing.T) (*UserInfoOtp, int32) {
	otpUser := testGenerateOtpUser(t, 0)
	len := otpUser.Throttle.Cliff - otpUser.Throttle.consErrorCounter
	otpUser.SetOtpUserBlockedState(false)
	return otpUser, len
}