detached 45e4796

test: add RevokeAllTokens error handling test cases

- Add test for ListTokens error propagation
- Add test for partial revoke failure (continues processing)
commit
45e4796435aaab39b49c9570d2e59f81f3b62094
author
brice · · 2026-01-17 09:22 UTC
parent
9ed3e41
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +60 -0
M internal/usecase/auth/service_test.go +60 -0
@@ -2,6 +2,7 @@ package auth
22
33 import (
44 "context"
5+ "errors"
56 "testing"
67 "time"
78
@@ -616,6 +617,65 @@ func TestService_RevokeAllTokens(t *testing.T) {
616617 require.NoError(t, err)
617618 assert.Equal(t, 0, count)
618619 })
620+
621+ t.Run("returns error when ListTokens fails", func(t *testing.T) {
622+ tokenStore := mocks.NewMockTokenStore(t)
623+
624+ tokenStore.EXPECT().
625+ ListTokens(mock.MatchedBy(func(ctx context.Context) bool { return ctx != nil })).
626+ Return(nil, errors.New("storage unavailable"))
627+
628+ svc := NewService(Config{
629+ Enabled: true,
630+ AuthType: domain.AuthTypeToken,
631+ }, tokenStore, zerowrap.Default())
632+
633+ ctx := testContext()
634+ count, err := svc.RevokeAllTokens(ctx)
635+
636+ require.Error(t, err)
637+ assert.Contains(t, err.Error(), "failed to list tokens")
638+ assert.Equal(t, 0, count)
639+ })
640+
641+ t.Run("continues on partial revoke failure", func(t *testing.T) {
642+ tokenStore := mocks.NewMockTokenStore(t)
643+
644+ tokens := []domain.Token{
645+ {ID: "token-1", Subject: "user1", Revoked: false},
646+ {ID: "token-2", Subject: "user2", Revoked: false},
647+ {ID: "token-3", Subject: "user3", Revoked: false},
648+ }
649+
650+ tokenStore.EXPECT().
651+ ListTokens(mock.MatchedBy(func(ctx context.Context) bool { return ctx != nil })).
652+ Return(tokens, nil)
653+
654+ // First token succeeds
655+ tokenStore.EXPECT().
656+ Revoke(mock.Anything, "token-1").
657+ Return(nil)
658+ // Second token fails
659+ tokenStore.EXPECT().
660+ Revoke(mock.Anything, "token-2").
661+ Return(errors.New("revoke failed"))
662+ // Third token succeeds
663+ tokenStore.EXPECT().
664+ Revoke(mock.Anything, "token-3").
665+ Return(nil)
666+
667+ svc := NewService(Config{
668+ Enabled: true,
669+ AuthType: domain.AuthTypeToken,
670+ }, tokenStore, zerowrap.Default())
671+
672+ ctx := testContext()
673+ count, err := svc.RevokeAllTokens(ctx)
674+
675+ // Should not return error, but count only successful revokes
676+ require.NoError(t, err)
677+ assert.Equal(t, 2, count)
678+ })
619679 }
620680
621681 func TestService_ListTokens(t *testing.T) {
@@ -2,6 +2,7 @@ package auth
2 2
3 import (3 import (
4 "context"4 "context"
5+ "errors"
5 "testing"6 "testing"
6 "time"7 "time"
7 8
@@ -616,6 +617,65 @@ func TestService_RevokeAllTokens(t *testing.T) {
616 require.NoError(t, err)617 require.NoError(t, err)
617 assert.Equal(t, 0, count)618 assert.Equal(t, 0, count)
618 })619 })
620+
621+ t.Run("returns error when ListTokens fails", func(t *testing.T) {
622+ tokenStore := mocks.NewMockTokenStore(t)
623+
624+ tokenStore.EXPECT().
625+ ListTokens(mock.MatchedBy(func(ctx context.Context) bool { return ctx != nil })).
626+ Return(nil, errors.New("storage unavailable"))
627+
628+ svc := NewService(Config{
629+ Enabled: true,
630+ AuthType: domain.AuthTypeToken,
631+ }, tokenStore, zerowrap.Default())
632+
633+ ctx := testContext()
634+ count, err := svc.RevokeAllTokens(ctx)
635+
636+ require.Error(t, err)
637+ assert.Contains(t, err.Error(), "failed to list tokens")
638+ assert.Equal(t, 0, count)
639+ })
640+
641+ t.Run("continues on partial revoke failure", func(t *testing.T) {
642+ tokenStore := mocks.NewMockTokenStore(t)
643+
644+ tokens := []domain.Token{
645+ {ID: "token-1", Subject: "user1", Revoked: false},
646+ {ID: "token-2", Subject: "user2", Revoked: false},
647+ {ID: "token-3", Subject: "user3", Revoked: false},
648+ }
649+
650+ tokenStore.EXPECT().
651+ ListTokens(mock.MatchedBy(func(ctx context.Context) bool { return ctx != nil })).
652+ Return(tokens, nil)
653+
654+ // First token succeeds
655+ tokenStore.EXPECT().
656+ Revoke(mock.Anything, "token-1").
657+ Return(nil)
658+ // Second token fails
659+ tokenStore.EXPECT().
660+ Revoke(mock.Anything, "token-2").
661+ Return(errors.New("revoke failed"))
662+ // Third token succeeds
663+ tokenStore.EXPECT().
664+ Revoke(mock.Anything, "token-3").
665+ Return(nil)
666+
667+ svc := NewService(Config{
668+ Enabled: true,
669+ AuthType: domain.AuthTypeToken,
670+ }, tokenStore, zerowrap.Default())
671+
672+ ctx := testContext()
673+ count, err := svc.RevokeAllTokens(ctx)
674+
675+ // Should not return error, but count only successful revokes
676+ require.NoError(t, err)
677+ assert.Equal(t, 2, count)
678+ })
619 }679 }
620 680
621 func TestService_ListTokens(t *testing.T) {681 func TestService_ListTokens(t *testing.T) {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close