summaryrefslogtreecommitdiff
path: root/internal/handlers/handler_webauthn_credentials_test.go
diff options
context:
space:
mode:
authorJames Elliott <james-d-elliott@users.noreply.github.com>2023-11-18 18:48:56 +1100
committerJames Elliott <james-d-elliott@users.noreply.github.com>2024-03-04 20:29:11 +1100
commit744b6179d28a12c69ae5a649f916806f5ddadfcd (patch)
treefe8516f9f2b5733cf8bb760b1944b13210661f2c /internal/handlers/handler_webauthn_credentials_test.go
parent85562a2465218273161cf9240ffebfe2ba6b187f (diff)
test(suites): add and fix tests for coverage
Add tests and adjust tests and code as appropriate. This also ensures we have thorough coverage of the code. Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
Diffstat (limited to 'internal/handlers/handler_webauthn_credentials_test.go')
-rw-r--r--internal/handlers/handler_webauthn_credentials_test.go605
1 files changed, 605 insertions, 0 deletions
diff --git a/internal/handlers/handler_webauthn_credentials_test.go b/internal/handlers/handler_webauthn_credentials_test.go
new file mode 100644
index 000000000..d95195f48
--- /dev/null
+++ b/internal/handlers/handler_webauthn_credentials_test.go
@@ -0,0 +1,605 @@
+package handlers
+
+import (
+ "fmt"
+ "net/mail"
+ "testing"
+
+ "github.com/golang/mock/gomock"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "github.com/valyala/fasthttp"
+
+ "github.com/authelia/authelia/v4/internal/authentication"
+ "github.com/authelia/authelia/v4/internal/mocks"
+ "github.com/authelia/authelia/v4/internal/model"
+ "github.com/authelia/authelia/v4/internal/storage"
+)
+
+func TestGetWebAuthnCredentialIDFromContext(t *testing.T) {
+ testCases := []struct {
+ name string
+ have any
+ expected int
+ err string
+ }{
+ {
+ "ShouldGetCredentialID",
+ "5",
+ 5,
+ "",
+ },
+ {
+ "ShouldNotParseInt",
+ 5,
+ 0,
+ "Invalid credential ID type",
+ },
+ {
+ "ShouldNotParseAlpha",
+ "abc",
+ 0,
+ "strconv.Atoi: parsing \"abc\": invalid syntax",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ mock := mocks.NewMockAutheliaCtx(t)
+
+ defer mock.Close()
+
+ if tc.have != nil {
+ mock.Ctx.SetUserValue("credentialID", tc.have)
+ }
+
+ actual, theErr := getWebAuthnCredentialIDFromContext(mock.Ctx)
+
+ if tc.err == "" {
+ assert.NoError(t, theErr)
+ assert.Equal(t, tc.expected, actual)
+ } else {
+ assert.Equal(t, 0, actual)
+ assert.EqualError(t, theErr, tc.err)
+ }
+ })
+ }
+}
+
+func TestWebAuthnCredentialsGET(t *testing.T) {
+ testCases := []struct {
+ name string
+ setup func(t *testing.T, mock *mocks.MockAutheliaCtx)
+ expected string
+ expectedStatus int
+ expectedf func(t *testing.T, mock *mocks.MockAutheliaCtx)
+ }{
+ {
+ "ShouldHandleNoCredentials",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ mock.StorageMock.EXPECT().LoadWebAuthnCredentialsByUsername(mock.Ctx, exampleDotCom, testUsername).Return(nil, storage.ErrNoWebAuthnCredential)
+ },
+ `{"status":"OK","data":null}`,
+ fasthttp.StatusOK,
+ nil,
+ },
+ {
+ "ShouldHandleAnonymous",
+ nil,
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred loading WebAuthn credentials", "user is anonymous")
+ },
+ },
+ {
+ "ShouldHandleBadOrigin",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ mock.Ctx.Request.Header.Set("X-Original-URL", "haoiu123!J@#*()!@HJ$!@*(OJOIFQJNW()D@JE()_@JK")
+ },
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred loading WebAuthn credentials for user 'john': error occurred attempting to retrieve origin", "failed to parse X-Original-URL header: parse \"haoiu123!J@#*()!@HJ$!@*(OJOIFQJNW()D@JE()_@JK\": invalid URI for request")
+ },
+ },
+ {
+ "ShouldHandleStorageError",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ mock.StorageMock.EXPECT().LoadWebAuthnCredentialsByUsername(mock.Ctx, exampleDotCom, testUsername).Return(nil, fmt.Errorf("bad block"))
+ },
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred loading WebAuthn credentials for user 'john'", "bad block")
+ },
+ },
+ {
+ "ShouldHandleCredentials",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ mock.StorageMock.EXPECT().LoadWebAuthnCredentialsByUsername(mock.Ctx, exampleDotCom, testUsername).Return([]model.WebAuthnCredential{{ID: 1}}, nil)
+ },
+ "{\"status\":\"OK\",\"data\":[{\"id\":1,\"created_at\":\"0001-01-01T00:00:00Z\",\"rpid\":\"\",\"username\":\"\",\"description\":\"\",\"kid\":\"\",\"attestation_type\":\"\",\"attachment\":\"\",\"transports\":null,\"sign_count\":0,\"clone_warning\":false,\"discoverable\":false,\"present\":false,\"verified\":false,\"backup_eligible\":false,\"backup_state\":false,\"public_key\":\"\"}]}",
+ fasthttp.StatusOK,
+ nil,
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ mock := mocks.NewMockAutheliaCtx(t)
+
+ defer mock.Close()
+
+ if tc.setup != nil {
+ tc.setup(t, mock)
+ }
+
+ WebAuthnCredentialsGET(mock.Ctx)
+
+ assert.Equal(t, tc.expectedStatus, mock.Ctx.Response.StatusCode())
+ assert.Equal(t, tc.expected, string(mock.Ctx.Response.Body()))
+
+ if tc.expectedf != nil {
+ tc.expectedf(t, mock)
+ }
+ })
+ }
+}
+
+func TestWebAuthnCredentialsPUT(t *testing.T) {
+ testCases := []struct {
+ name string
+ setup func(t *testing.T, mock *mocks.MockAutheliaCtx)
+ have string
+ expected string
+ expectedStatus int
+ expectedf func(t *testing.T, mock *mocks.MockAutheliaCtx)
+ }{
+ {
+ "ShouldHandleSuccessfulAdjustment",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ mock.StorageMock.EXPECT().LoadWebAuthnCredentialByID(mock.Ctx, 1).Return(&model.WebAuthnCredential{ID: 1, Username: testUsername}, nil)
+ mock.StorageMock.EXPECT().UpdateWebAuthnCredentialDescription(mock.Ctx, testUsername, 1, "abc").Return(nil)
+ },
+ `{"description":"abc"}`,
+ `{"status":"OK"}`,
+ fasthttp.StatusOK,
+ nil,
+ },
+ {
+ "ShouldHandleAnotherUser",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ mock.StorageMock.EXPECT().LoadWebAuthnCredentialByID(mock.Ctx, 1).Return(&model.WebAuthnCredential{ID: 1, Username: "anotheruser"}, nil)
+ },
+ `{"description":"abc"}`,
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john'", "user 'anotheruser' owns the credential with id '1'")
+ },
+ },
+ {
+ "ShouldHandleFailUpdate",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ mock.StorageMock.EXPECT().LoadWebAuthnCredentialByID(mock.Ctx, 1).Return(&model.WebAuthnCredential{ID: 1, Username: testUsername}, nil)
+ mock.StorageMock.EXPECT().UpdateWebAuthnCredentialDescription(mock.Ctx, testUsername, 1, "abc").Return(fmt.Errorf("gremlin"))
+ },
+ `{"description":"abc"}`,
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john': error occurred while attempting to save the modified credential in storage", "gremlin")
+ },
+ },
+ {
+ "ShouldHandleFailLoad",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ mock.StorageMock.EXPECT().LoadWebAuthnCredentialByID(mock.Ctx, 1).Return(nil, fmt.Errorf("deleted"))
+ },
+ `{"description":"abc"}`,
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john': error occurred trying to load the credential", "deleted")
+ },
+ },
+ {
+ "ShouldHandleBadJSON",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+ },
+ `{"description:"abc"}`,
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusBadRequest,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential: error occurred parsing the form data", "invalid character 'a' after object key")
+ },
+ },
+ {
+ "ShouldHandleBadDescription",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+ },
+ `{"description":""}`,
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john", "description is empty")
+ },
+ },
+ {
+ "ShouldHandleAnonymous",
+ nil,
+ `{"description":"abc"}`,
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential", "user is anonymous")
+ },
+ },
+ {
+ "ShouldHandleBadID",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ mock.Ctx.SetUserValue("credentialID", "a")
+ },
+ `{"description":"abc"}`,
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusBadRequest,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential for user 'john': error occurred trying to determine the credential ID", "strconv.Atoi: parsing \"a\": invalid syntax")
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ mock := mocks.NewMockAutheliaCtx(t)
+
+ defer mock.Close()
+
+ mock.Ctx.SetUserValue("credentialID", "1")
+ mock.Ctx.Request.SetBodyString(tc.have)
+
+ if tc.setup != nil {
+ tc.setup(t, mock)
+ }
+
+ WebAuthnCredentialPUT(mock.Ctx)
+
+ assert.Equal(t, tc.expectedStatus, mock.Ctx.Response.StatusCode())
+ assert.Equal(t, tc.expected, string(mock.Ctx.Response.Body()))
+
+ if tc.expectedf != nil {
+ tc.expectedf(t, mock)
+ }
+ })
+ }
+}
+
+func TestWebAuthnCredentialsDELETE(t *testing.T) {
+ testCases := []struct {
+ name string
+ setup func(t *testing.T, mock *mocks.MockAutheliaCtx)
+ expected string
+ expectedStatus int
+ expectedf func(t *testing.T, mock *mocks.MockAutheliaCtx)
+ }{
+ {
+ "ShouldHandleSuccessfulDelete",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ gomock.InOrder(
+ mock.StorageMock.EXPECT().
+ LoadWebAuthnCredentialByID(mock.Ctx, 1).
+ Return(&model.WebAuthnCredential{ID: 1, Username: testUsername, KID: model.NewBase64([]byte("abc"))}, nil),
+ mock.StorageMock.EXPECT().
+ DeleteWebAuthnCredential(mock.Ctx, model.NewBase64([]byte("abc")).String()).
+ Return(nil),
+ mock.UserProviderMock.EXPECT().
+ GetDetails(testUsername).
+ Return(&authentication.UserDetails{Username: testUsername, DisplayName: testDisplayName, Emails: []string{"john@example.com"}}, nil),
+ mock.NotifierMock.EXPECT().
+ Send(mock.Ctx, mail.Address{Name: testDisplayName, Address: "john@example.com"}, "Second Factor Method Removed", gomock.Any(), gomock.Any()).
+ Return(nil),
+ )
+ },
+ `{"status":"OK"}`,
+ fasthttp.StatusOK,
+ nil,
+ },
+ {
+ "ShouldHandleSuccessfulDeleteWithNotifierError",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ gomock.InOrder(
+ mock.StorageMock.EXPECT().
+ LoadWebAuthnCredentialByID(mock.Ctx, 1).
+ Return(&model.WebAuthnCredential{ID: 1, Username: testUsername, KID: model.NewBase64([]byte("abc"))}, nil),
+ mock.StorageMock.EXPECT().
+ DeleteWebAuthnCredential(mock.Ctx, model.NewBase64([]byte("abc")).String()).
+ Return(nil),
+ mock.UserProviderMock.EXPECT().
+ GetDetails(testUsername).
+ Return(&authentication.UserDetails{Username: testUsername, DisplayName: testDisplayName, Emails: []string{"john@example.com"}}, nil),
+ mock.NotifierMock.EXPECT().
+ Send(mock.Ctx, mail.Address{Name: testDisplayName, Address: "john@example.com"}, "Second Factor Method Removed", gomock.Any(), gomock.Any()).
+ Return(fmt.Errorf("bad conn")),
+ )
+ },
+ `{"status":"OK"}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred sending notification to user 'john' while attempting to notify them of an important event", "bad conn")
+ },
+ },
+ {
+ "ShouldHandleSuccessfulDeleteWithUserError",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ gomock.InOrder(
+ mock.StorageMock.EXPECT().
+ LoadWebAuthnCredentialByID(mock.Ctx, 1).
+ Return(&model.WebAuthnCredential{ID: 1, Username: testUsername, KID: model.NewBase64([]byte("abc"))}, nil),
+ mock.StorageMock.EXPECT().
+ DeleteWebAuthnCredential(mock.Ctx, model.NewBase64([]byte("abc")).String()).
+ Return(nil),
+ mock.UserProviderMock.EXPECT().
+ GetDetails(testUsername).
+ Return(nil, fmt.Errorf("bad user")),
+ )
+ },
+ `{"status":"OK"}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred looking up user details for user 'john' while attempting to notify them of an important event", "bad user")
+ },
+ },
+ {
+ "ShouldHandleFailedDeleteStorage",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ gomock.InOrder(
+ mock.StorageMock.EXPECT().
+ LoadWebAuthnCredentialByID(mock.Ctx, 1).
+ Return(&model.WebAuthnCredential{ID: 1, Username: testUsername, KID: model.NewBase64([]byte("abc"))}, nil),
+ mock.StorageMock.EXPECT().
+ DeleteWebAuthnCredential(mock.Ctx, model.NewBase64([]byte("abc")).String()).
+ Return(fmt.Errorf("bad pipe")),
+ )
+ },
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred delete WebAuthn credential for user 'john': error occurred while attempting to delete the credential from storage", "bad pipe")
+ },
+ },
+ {
+ "ShouldHandleFailedLoadStorage",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ gomock.InOrder(
+ mock.StorageMock.EXPECT().
+ LoadWebAuthnCredentialByID(mock.Ctx, 1).
+ Return(nil, fmt.Errorf("bad sql password")),
+ )
+ },
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred deleting WebAuthn credential for user 'john': error occurred trying to load the credential", "bad sql password")
+ },
+ },
+ {
+ "ShouldHandleBadUser",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ gomock.InOrder(
+ mock.StorageMock.EXPECT().
+ LoadWebAuthnCredentialByID(mock.Ctx, 1).
+ Return(&model.WebAuthnCredential{ID: 1, Username: "baduser", KID: model.NewBase64([]byte("abc"))}, nil),
+ )
+ },
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred deleting WebAuthn credential for user 'john'", "user 'baduser' owns the credential with id '1'")
+ },
+ },
+ {
+ "ShouldHandleBadID",
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ us, err := mock.Ctx.GetSession()
+
+ require.NoError(t, err)
+
+ us.Username = testUsername
+ us.AuthenticationLevel = authentication.OneFactor
+
+ require.NoError(t, mock.Ctx.SaveSession(us))
+
+ mock.Ctx.SetUserValue("credentialID", "a")
+ },
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusBadRequest,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred deleting WebAuthn credential: error occurred trying to determine the credential ID", "strconv.Atoi: parsing \"a\": invalid syntax")
+ },
+ },
+ {
+ "ShouldHandleAnonymous",
+ nil,
+ `{"status":"KO","message":"Operation failed."}`,
+ fasthttp.StatusOK,
+ func(t *testing.T, mock *mocks.MockAutheliaCtx) {
+ AssertLogEntryMessageAndError(t, mock.Hook.LastEntry(), "Error occurred modifying WebAuthn credential", "user is anonymous")
+ },
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ mock := mocks.NewMockAutheliaCtx(t)
+
+ defer mock.Close()
+
+ mock.Ctx.SetUserValue("credentialID", "1")
+
+ if tc.setup != nil {
+ tc.setup(t, mock)
+ }
+
+ WebAuthnCredentialDELETE(mock.Ctx)
+
+ assert.Equal(t, tc.expectedStatus, mock.Ctx.Response.StatusCode())
+ assert.Equal(t, tc.expected, string(mock.Ctx.Response.Body()))
+
+ if tc.expectedf != nil {
+ tc.expectedf(t, mock)
+ }
+ })
+ }
+}