Skip to content

Commit 134681e

Browse files
authored
test: increase test coverage for core package (#352)
* test(resource): add test cases for resource service * test(grant): add unit test for revoke * test(grant): add unit test for bulk revoke * test(policy): increase test coverage * test(provider): increase test coverage on provider pkg * chore: implement expecter in mock crypto
1 parent a01bb09 commit 134681e

File tree

5 files changed

+466
-8
lines changed

5 files changed

+466
-8
lines changed

core/grant/service_test.go

Lines changed: 194 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package grant_test
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"testing"
78
"time"
89

@@ -23,6 +24,8 @@ type ServiceTestSuite struct {
2324
mockRepository *mocks.Repository
2425
mockProviderService *mocks.ProviderService
2526
mockResourceService *mocks.ResourceService
27+
mockAuditLogger *mocks.AuditLogger
28+
mockNotifier *mocks.Notifier
2629
service *grant.Service
2730
}
2831

@@ -34,12 +37,16 @@ func (s *ServiceTestSuite) setup() {
3437
s.mockRepository = new(mocks.Repository)
3538
s.mockProviderService = new(mocks.ProviderService)
3639
s.mockResourceService = new(mocks.ResourceService)
40+
s.mockAuditLogger = new(mocks.AuditLogger)
41+
s.mockNotifier = new(mocks.Notifier)
3742
s.service = grant.NewService(grant.ServiceDeps{
3843
Repository: s.mockRepository,
3944
Logger: log.NewNoop(),
4045
Validator: validator.New(),
4146
ProviderService: s.mockProviderService,
4247
ResourceService: s.mockResourceService,
48+
Notifier: s.mockNotifier,
49+
AuditLogger: s.mockAuditLogger,
4350
})
4451
}
4552

@@ -122,6 +129,190 @@ func (s *ServiceTestSuite) TestGetByID() {
122129
})
123130
}
124131

132+
func (s *ServiceTestSuite) TestRevoke() {
133+
id := uuid.New().String()
134+
actor := "[email protected]"
135+
reason := "test reason"
136+
expectedGrantDetails := &domain.Grant{
137+
ID: id,
138+
AccountID: "test-account-id",
139+
AccountType: "user",
140+
Resource: &domain.Resource{
141+
ID: "test-resource-id",
142+
},
143+
}
144+
145+
s.Run("should revoke grant on success", func() {
146+
s.setup()
147+
148+
s.mockRepository.EXPECT().
149+
GetByID(mock.AnythingOfType("*context.emptyCtx"), id).
150+
Return(expectedGrantDetails, nil).Once()
151+
s.mockRepository.EXPECT().
152+
Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*domain.Grant")).
153+
Run(func(_a0 context.Context, _a1 *domain.Grant) {
154+
s.Equal(id, _a1.ID)
155+
s.Equal(actor, _a1.RevokedBy)
156+
s.Equal(reason, _a1.RevokeReason)
157+
s.NotNil(_a1.RevokedAt)
158+
}).
159+
Return(nil).Once()
160+
s.mockProviderService.EXPECT().
161+
RevokeAccess(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("domain.Grant")).
162+
Run(func(_a0 context.Context, _a1 domain.Grant) {
163+
s.Equal(id, _a1.ID)
164+
s.Equal(expectedGrantDetails.AccountID, _a1.AccountID)
165+
s.Equal(expectedGrantDetails.AccountType, _a1.AccountType)
166+
s.Equal(expectedGrantDetails.Resource.ID, _a1.Resource.ID)
167+
}).
168+
Return(nil).Once()
169+
170+
s.mockNotifier.EXPECT().
171+
Notify([]domain.Notification{{
172+
User: expectedGrantDetails.CreatedBy,
173+
Message: domain.NotificationMessage{
174+
Type: domain.NotificationTypeAccessRevoked,
175+
Variables: map[string]interface{}{
176+
"resource_name": fmt.Sprintf("%s (%s: %s)", expectedGrantDetails.Resource.Name, expectedGrantDetails.Resource.ProviderType, expectedGrantDetails.Resource.URN),
177+
"role": expectedGrantDetails.Role,
178+
"account_type": expectedGrantDetails.AccountType,
179+
"account_id": expectedGrantDetails.AccountID,
180+
"requestor": expectedGrantDetails.Owner,
181+
},
182+
},
183+
}}).
184+
Return(nil).Once()
185+
s.mockAuditLogger.EXPECT().
186+
Log(mock.AnythingOfType("*context.emptyCtx"), grant.AuditKeyRevoke, map[string]interface{}{
187+
"grant_id": id,
188+
"reason": reason,
189+
}).
190+
Return(nil).Once()
191+
192+
expectedGrant, err := s.service.Revoke(context.Background(), id, actor, reason)
193+
194+
s.NoError(err)
195+
s.Equal(id, expectedGrant.ID)
196+
s.Equal(actor, expectedGrant.RevokedBy)
197+
s.Equal(reason, expectedGrant.RevokeReason)
198+
s.NotNil(expectedGrant.RevokedAt)
199+
s.Less(*expectedGrant.RevokedAt, time.Now())
200+
s.mockRepository.AssertExpectations(s.T())
201+
})
202+
203+
s.Run("should skip revoke in provider and notifications as configured", func() {
204+
s.setup()
205+
206+
s.mockRepository.EXPECT().
207+
GetByID(mock.AnythingOfType("*context.emptyCtx"), id).
208+
Return(expectedGrantDetails, nil).Once()
209+
s.mockRepository.EXPECT().
210+
Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*domain.Grant")).
211+
Run(func(_a0 context.Context, _a1 *domain.Grant) {
212+
s.Equal(id, _a1.ID)
213+
s.Equal(actor, _a1.RevokedBy)
214+
s.Equal(reason, _a1.RevokeReason)
215+
s.NotNil(_a1.RevokedAt)
216+
}).
217+
Return(nil).Once()
218+
219+
s.mockAuditLogger.EXPECT().
220+
Log(mock.AnythingOfType("*context.emptyCtx"), grant.AuditKeyRevoke, map[string]interface{}{
221+
"grant_id": id,
222+
"reason": reason,
223+
}).
224+
Return(nil).Once()
225+
226+
expectedGrant, err := s.service.Revoke(context.Background(), id, actor, reason, grant.SkipRevokeAccessInProvider(), grant.SkipNotifications())
227+
228+
s.NoError(err)
229+
s.Equal(id, expectedGrant.ID)
230+
s.Equal(actor, expectedGrant.RevokedBy)
231+
s.Equal(reason, expectedGrant.RevokeReason)
232+
s.NotNil(expectedGrant.RevokedAt)
233+
s.Less(*expectedGrant.RevokedAt, time.Now())
234+
s.mockRepository.AssertExpectations(s.T())
235+
})
236+
}
237+
238+
func (s *ServiceTestSuite) TestBulkRevoke() {
239+
actor := "[email protected]"
240+
reason := "test reason"
241+
filter := domain.RevokeGrantsFilter{
242+
AccountIDs: []string{"test-account-id"},
243+
}
244+
expectedGrants := []domain.Grant{
245+
{
246+
ID: "id1",
247+
AccountID: "test-account-id",
248+
AccountType: "user",
249+
Resource: &domain.Resource{
250+
ID: "test-resource-id",
251+
},
252+
},
253+
{
254+
ID: "id2",
255+
AccountID: "test-account-id",
256+
AccountType: "user",
257+
Resource: &domain.Resource{
258+
ID: "test-resource-id",
259+
},
260+
},
261+
}
262+
263+
s.Run("should return revoked grants on success", func() {
264+
s.setup()
265+
266+
expectedListGrantsFilter := domain.ListGrantsFilter{
267+
Statuses: []string{string(domain.GrantStatusActive)},
268+
AccountIDs: filter.AccountIDs,
269+
ProviderTypes: filter.ProviderTypes,
270+
ProviderURNs: filter.ProviderURNs,
271+
ResourceTypes: filter.ResourceTypes,
272+
ResourceURNs: filter.ResourceURNs,
273+
}
274+
275+
s.mockRepository.EXPECT().
276+
List(mock.AnythingOfType("*context.emptyCtx"), expectedListGrantsFilter).
277+
Return(expectedGrants, nil).Once()
278+
for _, g := range expectedGrants {
279+
grant := g
280+
s.mockProviderService.EXPECT().
281+
RevokeAccess(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("domain.Grant")).
282+
Run(func(_a0 context.Context, _a1 domain.Grant) {
283+
s.Equal(grant.ID, _a1.ID)
284+
s.Equal(grant.AccountID, _a1.AccountID)
285+
s.Equal(grant.AccountType, _a1.AccountType)
286+
s.Equal(grant.Resource.ID, _a1.Resource.ID)
287+
}).
288+
Return(nil).Once()
289+
290+
s.mockRepository.EXPECT().
291+
Update(mock.AnythingOfType("*context.emptyCtx"), mock.AnythingOfType("*domain.Grant")).
292+
Run(func(_a0 context.Context, _a1 *domain.Grant) {
293+
s.Equal(grant.ID, _a1.ID)
294+
s.Equal(actor, _a1.RevokedBy)
295+
s.Equal(reason, _a1.RevokeReason)
296+
s.NotNil(_a1.RevokedAt)
297+
}).
298+
Return(nil).Once()
299+
}
300+
301+
revokedGrants, actualError := s.service.BulkRevoke(context.Background(), filter, actor, reason)
302+
303+
s.NoError(actualError)
304+
for i, g := range revokedGrants {
305+
revokedGrant := g
306+
expectedGrant := expectedGrants[i]
307+
s.Equal(expectedGrant.ID, revokedGrant.ID)
308+
s.Equal(actor, revokedGrant.RevokedBy)
309+
s.Equal(reason, revokedGrant.RevokeReason)
310+
s.NotNil(revokedGrant.RevokedAt)
311+
s.Less(*revokedGrant.RevokedAt, time.Now())
312+
}
313+
})
314+
}
315+
125316
func (s *ServiceTestSuite) TestPrepare() {
126317
s.Run("should return error if appeal is invalid", func() {
127318
testCases := []struct {
@@ -339,7 +530,7 @@ func (s *ServiceTestSuite) TestImportFromProvider() {
339530
{
340531
AccountID: "test-account-id-2",
341532
AccountType: "serviceAccount",
342-
Permission: "test-permission",
533+
Permission: "test-permission-2",
343534
},
344535
},
345536
},
@@ -362,8 +553,8 @@ func (s *ServiceTestSuite) TestImportFromProvider() {
362553
ResourceID: "test-resource-id",
363554
AccountID: "test-account-id-2",
364555
AccountType: "serviceAccount",
365-
Role: "test-role-id",
366-
Permissions: []string{"test-permission"},
556+
Role: "test-permission-2",
557+
Permissions: []string{"test-permission-2"},
367558
IsPermanent: true,
368559
Status: domain.GrantStatusActive,
369560
StatusInProvider: domain.GrantStatusActive,

core/policy/service_test.go

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type ServiceTestSuite struct {
2424
mockResourceService *policymocks.ResourceService
2525
mockProviderService *policymocks.ProviderService
2626
mockAuditLogger *policymocks.AuditLogger
27+
mockCrypto *mocks.Crypto
2728
service *policy.Service
2829
}
2930

@@ -33,9 +34,9 @@ func (s *ServiceTestSuite) SetupTest() {
3334
s.mockProviderService = new(policymocks.ProviderService)
3435
s.mockAuditLogger = new(policymocks.AuditLogger)
3536

36-
mockCrypto := new(mocks.Crypto)
37+
s.mockCrypto = new(mocks.Crypto)
3738
v := validator.New()
38-
iamManager := identities.NewManager(mockCrypto, v)
39+
iamManager := identities.NewManager(s.mockCrypto, v)
3940

4041
s.service = policy.NewService(policy.ServiceDeps{
4142
Repository: s.mockPolicyRepository,
@@ -325,11 +326,24 @@ func (s *ServiceTestSuite) TestCreate() {
325326
AllowPermanentAccess: false,
326327
AllowActiveAccessExtensionIn: "24h",
327328
},
329+
IAM: &domain.IAMConfig{
330+
Provider: "http",
331+
Config: map[string]interface{}{
332+
"url": "http://test-localhost:8080",
333+
"auth": map[string]interface{}{
334+
"type": "basic",
335+
"username": "test-user",
336+
"password": "test-password",
337+
},
338+
},
339+
},
328340
}
329341

330342
s.Run("should return error if got error from the policy repository", func() {
331343
expectedError := errors.New("error from repository")
332344
s.mockPolicyRepository.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), mock.Anything).Return(expectedError).Once()
345+
s.mockCrypto.EXPECT().Encrypt("test-password").Return("test-password", nil).Once()
346+
s.mockCrypto.EXPECT().Decrypt("test-password").Return("test-password", nil).Once()
333347

334348
actualError := s.service.Create(context.Background(), validPolicy)
335349

@@ -344,6 +358,8 @@ func (s *ServiceTestSuite) TestCreate() {
344358

345359
expectedVersion := uint(1)
346360
s.mockPolicyRepository.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), p).Return(nil).Once()
361+
s.mockCrypto.EXPECT().Encrypt("test-password").Return("test-password", nil).Once()
362+
s.mockCrypto.EXPECT().Decrypt("test-password").Return("test-password", nil).Once()
347363
s.mockAuditLogger.EXPECT().Log(mock.Anything, policy.AuditKeyPolicyCreate, mock.Anything).Return(nil).Once()
348364

349365
actualError := s.service.Create(context.Background(), p)
@@ -357,6 +373,8 @@ func (s *ServiceTestSuite) TestCreate() {
357373
s.Run("should pass the model from the param", func() {
358374
s.mockPolicyRepository.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), validPolicy).Return(nil).Once()
359375
s.mockAuditLogger.EXPECT().Log(mock.Anything, policy.AuditKeyPolicyCreate, mock.Anything).Return(nil).Once()
376+
s.mockCrypto.EXPECT().Encrypt("test-password").Return("test-password", nil).Once()
377+
s.mockCrypto.EXPECT().Decrypt("test-password").Return("test-password", nil).Once()
360378

361379
actualError := s.service.Create(context.Background(), validPolicy)
362380

@@ -628,8 +646,23 @@ func (s *ServiceTestSuite) TestFind() {
628646
})
629647

630648
s.Run("should return list of records on success", func() {
631-
expectedResult := []*domain.Policy{}
649+
expectedResult := []*domain.Policy{
650+
{
651+
IAM: &domain.IAMConfig{
652+
Provider: "http",
653+
Config: map[string]interface{}{
654+
"url": "http://test-localhost:8080",
655+
"auth": map[string]interface{}{
656+
"type": "basic",
657+
"username": "test-user",
658+
"password": "test-password",
659+
},
660+
},
661+
},
662+
},
663+
}
632664
s.mockPolicyRepository.EXPECT().Find(mock.AnythingOfType("*context.emptyCtx")).Return(expectedResult, nil).Once()
665+
s.mockCrypto.EXPECT().Decrypt("test-password").Return("test-password", nil).Once()
633666

634667
actualResult, actualError := s.service.Find(context.Background())
635668

@@ -651,8 +684,21 @@ func (s *ServiceTestSuite) TestGetOne() {
651684
})
652685

653686
s.Run("should return list of records on success", func() {
654-
expectedResult := &domain.Policy{}
687+
expectedResult := &domain.Policy{
688+
IAM: &domain.IAMConfig{
689+
Provider: "http",
690+
Config: map[string]interface{}{
691+
"url": "http://test-localhost:8080",
692+
"auth": map[string]interface{}{
693+
"type": "basic",
694+
"username": "test-user",
695+
"password": "test-password",
696+
},
697+
},
698+
},
699+
}
655700
s.mockPolicyRepository.EXPECT().GetOne(mock.AnythingOfType("*context.emptyCtx"), mock.Anything, mock.Anything).Return(expectedResult, nil).Once()
701+
s.mockCrypto.EXPECT().Decrypt("test-password").Return("test-password", nil).Once()
656702

657703
actualResult, actualError := s.service.GetOne(context.Background(), "", 0)
658704

@@ -685,6 +731,17 @@ func (s *ServiceTestSuite) TestUpdate() {
685731
},
686732
},
687733
},
734+
IAM: &domain.IAMConfig{
735+
Provider: "http",
736+
Config: map[string]interface{}{
737+
"url": "http://test-localhost:8080",
738+
"auth": map[string]interface{}{
739+
"type": "basic",
740+
"username": "test-user",
741+
"password": "test-password",
742+
},
743+
},
744+
},
688745
}
689746

690747
expectedLatestPolicy := &domain.Policy{
@@ -694,6 +751,8 @@ func (s *ServiceTestSuite) TestUpdate() {
694751
expectedNewVersion := uint(6)
695752
s.mockPolicyRepository.EXPECT().GetOne(mock.AnythingOfType("*context.emptyCtx"), p.ID, uint(0)).Return(expectedLatestPolicy, nil).Once()
696753
s.mockPolicyRepository.EXPECT().Create(mock.AnythingOfType("*context.emptyCtx"), p).Return(nil)
754+
s.mockCrypto.EXPECT().Encrypt("test-password").Return("test-password", nil).Once()
755+
s.mockCrypto.EXPECT().Decrypt("test-password").Return("test-password", nil).Once()
697756
s.mockAuditLogger.EXPECT().Log(mock.Anything, policy.AuditKeyPolicyUpdate, mock.Anything).Return(nil).Once()
698757

699758
s.service.Update(context.Background(), p)

0 commit comments

Comments
 (0)