Skip to content

Commit

Permalink
Prepare teleport access plugin enterprise test suite (#40479)
Browse files Browse the repository at this point in the history
* Make AuthHelper support enterprise

* Split access OSS and Enterprise tets suites

* fix slack tests

* fix race in accesslist reminder tests

* fixup! fix race in accesslist reminder tests
  • Loading branch information
hugoShaka authored Apr 11, 2024
1 parent 607d2e4 commit a24216e
Show file tree
Hide file tree
Showing 55 changed files with 483 additions and 395 deletions.
1 change: 0 additions & 1 deletion integrations/access/accesslist/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ func (a *App) run(ctx context.Context) error {
if err := a.remindIfNecessary(ctx); err != nil {
return trace.Wrap(err)
}

timer.Reset(jitter(reminderInterval))
case <-ctx.Done():
log.Info("Access list monitor is finished")
Expand Down
23 changes: 20 additions & 3 deletions integrations/access/accesslist/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package accesslist

import (
"context"
"sync"
"testing"
"time"

Expand All @@ -40,18 +41,35 @@ import (
type mockMessagingBot struct {
lastReminderRecipients []common.Recipient
recipients map[string]*common.Recipient
mutex sync.Mutex
}

func (m *mockMessagingBot) CheckHealth(ctx context.Context) error {
return nil
}

func (m *mockMessagingBot) SendReviewReminders(ctx context.Context, recipient common.Recipient, accessList *accesslist.AccessList) error {
m.mutex.Lock()
defer m.mutex.Unlock()
m.lastReminderRecipients = append(m.lastReminderRecipients, recipient)
return nil
}

func (m *mockMessagingBot) getLastRecipients() []common.Recipient {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.lastReminderRecipients
}

func (m *mockMessagingBot) resetLastRecipients() {
m.mutex.Lock()
defer m.mutex.Unlock()
m.lastReminderRecipients = make([]common.Recipient, 0)
}

func (m *mockMessagingBot) FetchRecipient(ctx context.Context, recipient string) (*common.Recipient, error) {
m.mutex.Lock()
defer m.mutex.Unlock()
fetchedRecipient, ok := m.recipients[recipient]
if !ok {
return nil, trace.NotFound("recipient %s not found", recipient)
Expand Down Expand Up @@ -255,14 +273,13 @@ func advanceAndLookForRecipients(t *testing.T,
advance time.Duration,
accessList *accesslist.AccessList,
recipients ...string) {
t.Helper()

ctx := context.Background()

_, err := alSvc.UpsertAccessList(ctx, accessList)
require.NoError(t, err)

bot.lastReminderRecipients = nil
bot.resetLastRecipients()

var expectedRecipients []common.Recipient
if len(recipients) > 0 {
Expand All @@ -274,5 +291,5 @@ func advanceAndLookForRecipients(t *testing.T,
clock.Advance(advance)
clock.BlockUntil(1)

require.ElementsMatch(t, expectedRecipients, bot.lastReminderRecipients)
require.ElementsMatch(t, expectedRecipients, bot.getLastRecipients())
}
2 changes: 1 addition & 1 deletion integrations/access/discord/testlib/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
"github.com/gravitational/teleport/integrations/access/discord"
)

func (s *DiscordSuite) checkPluginData(ctx context.Context, reqID string, cond func(accessrequest.PluginData) bool) accessrequest.PluginData {
func (s *DiscordBaseSuite) checkPluginData(ctx context.Context, reqID string, cond func(accessrequest.PluginData) bool) accessrequest.PluginData {
t := s.T()
t.Helper()

Expand Down
8 changes: 5 additions & 3 deletions integrations/access/discord/testlib/oss_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ import (
)

func TestDiscordPluginOSS(t *testing.T) {
discordSuite := &DiscordSuite{
AccessRequestSuite: &integration.AccessRequestSuite{
AuthHelper: &integration.OSSAuthHelper{},
discordSuite := &DiscordSuiteOSS{
DiscordBaseSuite: DiscordBaseSuite{
AccessRequestSuite: &integration.AccessRequestSuite{
AuthHelper: &integration.MinimalAuthHelper{},
},
},
}
suite.Run(t, discordSuite)
Expand Down
60 changes: 32 additions & 28 deletions integrations/access/discord/testlib/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ import (
var msgFieldRegexp = regexp.MustCompile(`(?im)^\*([a-zA-Z ]+)\*: (.+)$`)
var requestReasonRegexp = regexp.MustCompile("(?im)^\\*Reason\\*:\\ ```\\n(.*?)```(.*?)$")

// DiscordSuite is the discord access plugin test suite.
// DiscordBaseSuite is the discord access plugin test suite.
// It implements the testify.TestingSuite interface.
type DiscordSuite struct {
type DiscordBaseSuite struct {
*integration.AccessRequestSuite
appConfig *discord.Config
raceNumber int
Expand All @@ -56,7 +56,7 @@ type DiscordSuite struct {

// SetupTest starts a fake discord and generates the plugin configuration.
// It is run for each test.
func (s *DiscordSuite) SetupTest() {
func (s *DiscordBaseSuite) SetupTest() {
t := s.T()

err := logger.Setup(logger.Config{Severity: "debug"})
Expand All @@ -75,18 +75,38 @@ func (s *DiscordSuite) SetupTest() {
}

// startApp starts the discord plugin, waits for it to become ready and returns,
func (s *DiscordSuite) startApp() {
func (s *DiscordBaseSuite) startApp() {
t := s.T()
t.Helper()

app := discord.NewApp(s.appConfig)
integration.RunAndWaitReady(t, app)
}

// DiscordSuiteOSS contains all tests that support running against a Teleport
// OSS Server.
type DiscordSuiteOSS struct {
DiscordBaseSuite
}

// DiscordSuiteEnterprise contains all tests that require a Teleport Enterprise
// to run.
type DiscordSuiteEnterprise struct {
DiscordBaseSuite
}

// SetupTest overrides DiscordBaseSuite.SetupTest to check the Teleport features
// before each test.
func (s *DiscordSuiteEnterprise) SetupTest() {
t := s.T()
s.RequireAdvancedWorkflow(t)
s.DiscordBaseSuite.SetupTest()
}

// TestMessagePosting validates that a message is sent to each recipient
// specified in the plugin's configuration. It also checks that the message
// content is correct.
func (s *DiscordSuite) TestMessagePosting() {
func (s *DiscordSuiteOSS) TestMessagePosting() {
t := s.T()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
t.Cleanup(cancel)
Expand Down Expand Up @@ -151,7 +171,7 @@ func (s *DiscordSuite) TestMessagePosting() {

// TestApproval tests that when a request is approved, its corresponding message
// is updated to reflect the new request state.
func (s *DiscordSuite) TestApproval() {
func (s *DiscordSuiteOSS) TestApproval() {
t := s.T()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
t.Cleanup(cancel)
Expand Down Expand Up @@ -189,7 +209,7 @@ func (s *DiscordSuite) TestApproval() {

// TestDenial tests that when a request is denied, its corresponding message
// is updated to reflect the new request state.
func (s *DiscordSuite) TestDenial() {
func (s *DiscordSuiteOSS) TestDenial() {
t := s.T()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
t.Cleanup(cancel)
Expand Down Expand Up @@ -228,15 +248,11 @@ func (s *DiscordSuite) TestDenial() {

// TestReviewUpdates tests that the message is updated after the access request
// is reviewed. Each review should be reflected in the original message.
func (s *DiscordSuite) TestReviewUpdates() {
func (s *DiscordSuiteEnterprise) TestReviewUpdates() {
t := s.T()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
t.Cleanup(cancel)

if !s.TeleportFeatures().AdvancedAccessWorkflows {
t.Skip("Doesn't work in OSS version")
}

s.appConfig.Recipients = common.RawRecipientsMap{
"editor": []string{
"1001", // reviewer 1
Expand Down Expand Up @@ -295,15 +311,11 @@ func (s *DiscordSuite) TestReviewUpdates() {

// TestApprovalByReview tests that the message is updated after the access request
// is reviewed and approved.
func (s *DiscordSuite) TestApprovalByReview() {
func (s *DiscordSuiteEnterprise) TestApprovalByReview() {
t := s.T()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
t.Cleanup(cancel)

if !s.TeleportFeatures().AdvancedAccessWorkflows {
t.Skip("Doesn't work in OSS version")
}

s.appConfig.Recipients = common.RawRecipientsMap{
"editor": []string{
"1001", // reviewer 1
Expand Down Expand Up @@ -363,15 +375,11 @@ func (s *DiscordSuite) TestApprovalByReview() {

// TestDenialByReview tests that the message is updated after the access request
// is reviewed and denied.
func (s *DiscordSuite) TestDenialByReview() {
func (s *DiscordSuiteEnterprise) TestDenialByReview() {
t := s.T()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
t.Cleanup(cancel)

if !s.TeleportFeatures().AdvancedAccessWorkflows {
t.Skip("Doesn't work in OSS version")
}

s.appConfig.Recipients = common.RawRecipientsMap{
"editor": []string{
"1001", // reviewer 1
Expand Down Expand Up @@ -431,7 +439,7 @@ func (s *DiscordSuite) TestDenialByReview() {

// TestExpiration tests that when a request expires, its corresponding message
// is updated to reflect the new request state.
func (s *DiscordSuite) TestExpiration() {
func (s *DiscordSuiteOSS) TestExpiration() {
t := s.T()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
t.Cleanup(cancel)
Expand Down Expand Up @@ -475,15 +483,11 @@ func (s *DiscordSuite) TestExpiration() {
// TestRace validates that the plugin behaves properly and performs all the
// message updates when a lot of access requests are sent and reviewed in a very
// short time frame.
func (s *DiscordSuite) TestRace() {
func (s *DiscordSuiteEnterprise) TestRace() {
t := s.T()
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
t.Cleanup(cancel)

if !s.TeleportFeatures().AdvancedAccessWorkflows {
t.Skip("Doesn't work in OSS version")
}

err := logger.Setup(logger.Config{Severity: "info"}) // Turn off noisy debug logging
require.NoError(t, err)

Expand Down
8 changes: 4 additions & 4 deletions integrations/access/email/testlib/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
"github.com/gravitational/teleport/integrations/access/email"
)

func (s *EmailSuite) checkPluginData(ctx context.Context, reqID string, cond func(email.PluginData) bool) email.PluginData {
func (s *EmailBaseSuite) checkPluginData(ctx context.Context, reqID string, cond func(email.PluginData) bool) email.PluginData {
t := s.T()
t.Helper()

Expand All @@ -40,15 +40,15 @@ func (s *EmailSuite) checkPluginData(ctx context.Context, reqID string, cond fun
}

// skipEmails ensures that emails were received, but dumps the contents
func (s *EmailSuite) skipMessages(ctx context.Context, t *testing.T, n int) {
func (s *EmailBaseSuite) skipMessages(ctx context.Context, t *testing.T, n int) {
for i := 0; i < n; i++ {
_, err := s.mockMailgun.GetMessage(ctx)
require.NoError(t, err)
}
}

// getMessages returns next n email messages
func (s *EmailSuite) getMessages(ctx context.Context, t *testing.T, n int) []mockMailgunMessage {
func (s *EmailBaseSuite) getMessages(ctx context.Context, t *testing.T, n int) []mockMailgunMessage {
messages := make([]mockMailgunMessage, n)
for i := 0; i < n; i++ {
m, err := s.mockMailgun.GetMessage(ctx)
Expand All @@ -60,7 +60,7 @@ func (s *EmailSuite) getMessages(ctx context.Context, t *testing.T, n int) []moc
}

// extractRequestID extracts request id from a subject
func (s *EmailSuite) extractRequestID(subject string) string {
func (s *EmailBaseSuite) extractRequestID(subject string) string {
idx := strings.Index(subject, subjectIDSubstring)
return subject[idx+len(subjectIDSubstring):]
}
8 changes: 5 additions & 3 deletions integrations/access/email/testlib/oss_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ import (
)

func TestEmailPluginOSS(t *testing.T) {
emailSuite := &EmailSuite{
AccessRequestSuite: &integration.AccessRequestSuite{
AuthHelper: &integration.OSSAuthHelper{},
emailSuite := &EmailSuiteOSS{
EmailBaseSuite{
AccessRequestSuite: &integration.AccessRequestSuite{
AuthHelper: &integration.MinimalAuthHelper{},
},
},
}
suite.Run(t, emailSuite)
Expand Down
Loading

0 comments on commit a24216e

Please sign in to comment.