github.com/chenbh/concourse/v6@v6.4.2/atc/lidar/checker_test.go (about)

     1  package lidar_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"time"
     7  
     8  	"code.cloudfoundry.org/lager/lagertest"
     9  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/gomega"
    11  
    12  	"github.com/chenbh/concourse/v6/atc/db"
    13  	"github.com/chenbh/concourse/v6/atc/db/dbfakes"
    14  	"github.com/chenbh/concourse/v6/atc/engine"
    15  	"github.com/chenbh/concourse/v6/atc/engine/enginefakes"
    16  	"github.com/chenbh/concourse/v6/atc/lidar"
    17  	"github.com/chenbh/concourse/v6/atc/lidar/lidarfakes"
    18  	"github.com/chenbh/concourse/v6/tracing"
    19  	"go.opentelemetry.io/otel/api/trace"
    20  	"go.opentelemetry.io/otel/api/trace/testtrace"
    21  )
    22  
    23  type Checker interface {
    24  	Run(context.Context) error
    25  }
    26  
    27  var _ = Describe("Checker", func() {
    28  	var (
    29  		err error
    30  
    31  		fakeCheckFactory   *dbfakes.FakeCheckFactory
    32  		fakeEngine         *enginefakes.FakeEngine
    33  		fakeRateCalculator *lidarfakes.FakeRateCalculator
    34  		fakeLimiter        *lidarfakes.FakeLimiter
    35  
    36  		checker Checker
    37  		logger  *lagertest.TestLogger
    38  	)
    39  
    40  	BeforeEach(func() {
    41  		fakeCheckFactory = new(dbfakes.FakeCheckFactory)
    42  		fakeEngine = new(enginefakes.FakeEngine)
    43  		fakeRateCalculator = new(lidarfakes.FakeRateCalculator)
    44  		fakeLimiter = new(lidarfakes.FakeLimiter)
    45  
    46  		logger = lagertest.NewTestLogger("test")
    47  	})
    48  
    49  	JustBeforeEach(func() {
    50  		checker = lidar.NewChecker(
    51  			logger,
    52  			fakeCheckFactory,
    53  			fakeEngine,
    54  			fakeRateCalculator,
    55  		)
    56  
    57  		err = checker.Run(context.TODO())
    58  	})
    59  
    60  	Describe("Run", func() {
    61  		Context("when retrieving checks fails", func() {
    62  			BeforeEach(func() {
    63  				fakeCheckFactory.StartedChecksReturns(nil, errors.New("nope"))
    64  			})
    65  
    66  			It("errors", func() {
    67  				Expect(err).To(HaveOccurred())
    68  			})
    69  		})
    70  
    71  		Context("when tracing is configured", func() {
    72  			var (
    73  				scanSpan     trace.Span
    74  				fakeRunnable *enginefakes.FakeRunnable
    75  			)
    76  
    77  			BeforeEach(func() {
    78  				tracing.ConfigureTraceProvider(&tracing.TestTraceProvider{})
    79  				fakeCheck := new(dbfakes.FakeCheck)
    80  				fakeCheck.IDReturns(1)
    81  				var ctx context.Context
    82  				ctx, scanSpan = tracing.StartSpan(context.Background(), "fake-operation", nil)
    83  				fakeCheck.SpanContextReturns(db.NewSpanContext(ctx))
    84  
    85  				fakeCheckFactory.StartedChecksReturns([]db.Check{
    86  					fakeCheck,
    87  				}, nil)
    88  
    89  				fakeLimiter.WaitReturns(nil)
    90  				fakeRateCalculator.RateLimiterReturns(fakeLimiter, nil)
    91  
    92  				fakeRunnable = new(enginefakes.FakeRunnable)
    93  				fakeEngine.NewCheckReturns(fakeRunnable)
    94  			})
    95  
    96  			AfterEach(func() {
    97  				tracing.Configured = false
    98  			})
    99  
   100  			It("propagates span context to check step", func() {
   101  				Eventually(fakeRunnable.RunCallCount).Should(Equal(1))
   102  				ctx := fakeRunnable.RunArgsForCall(0)
   103  				span, ok := tracing.FromContext(ctx).(*testtrace.Span)
   104  				Expect(ok).To(BeTrue(), "no testtrace.Span in context")
   105  				Expect(span.ParentSpanID()).To(Equal(scanSpan.SpanContext().SpanID))
   106  			})
   107  		})
   108  
   109  		Context("when retrieving checks succeeds", func() {
   110  			var fakeCheck1, fakeCheck2, fakeCheck3 *dbfakes.FakeCheck
   111  
   112  			BeforeEach(func() {
   113  				fakeCheck1 = new(dbfakes.FakeCheck)
   114  				fakeCheck1.IDReturns(1)
   115  				fakeCheck2 = new(dbfakes.FakeCheck)
   116  				fakeCheck2.IDReturns(2)
   117  				fakeCheck3 = new(dbfakes.FakeCheck)
   118  				fakeCheck3.IDReturns(3)
   119  
   120  				fakeCheckFactory.StartedChecksReturns([]db.Check{
   121  					fakeCheck1,
   122  					fakeCheck2,
   123  					fakeCheck3,
   124  				}, nil)
   125  
   126  				fakeEngine.NewCheckStub = func(check db.Check) engine.Runnable {
   127  					time.Sleep(time.Second)
   128  					return new(enginefakes.FakeRunnable)
   129  				}
   130  			})
   131  
   132  			Context("when the rate limiter is fetched correctly", func() {
   133  				BeforeEach(func() {
   134  					fakeLimiter.WaitReturns(nil)
   135  					fakeRateCalculator.RateLimiterReturns(fakeLimiter, nil)
   136  				})
   137  
   138  				It("succeeds", func() {
   139  					Expect(err).NotTo(HaveOccurred())
   140  				})
   141  
   142  				It("runs all pending checks", func() {
   143  					Eventually(fakeEngine.NewCheckCallCount).Should(Equal(3))
   144  				})
   145  
   146  				It("rate limits all the checks", func() {
   147  					Eventually(fakeLimiter.WaitCallCount()).Should(Equal(3))
   148  				})
   149  
   150  				Context("when there is a manually triggered check and the rate limiter is not allowing any checks to run", func() {
   151  					BeforeEach(func() {
   152  						fakeCheck1.ManuallyTriggeredReturns(true)
   153  
   154  						fakeLimiter.WaitReturns(errors.New("not-allowed"))
   155  					})
   156  
   157  					It("runs the manually triggered check", func() {
   158  						Eventually(fakeEngine.NewCheckCallCount).Should(Equal(1))
   159  						Expect(fakeEngine.NewCheckArgsForCall(0).ID()).To(Equal(fakeCheck1.ID()))
   160  					})
   161  				})
   162  			})
   163  
   164  			Context("when calculating the rate limit fails", func() {
   165  				BeforeEach(func() {
   166  					fakeRateCalculator.RateLimiterReturns(nil, errors.New("disaster"))
   167  				})
   168  
   169  				It("errors", func() {
   170  					Expect(err).To(HaveOccurred())
   171  				})
   172  			})
   173  		})
   174  
   175  		Context("when a check is already running", func() {
   176  			BeforeEach(func() {
   177  				fakeCheck := new(dbfakes.FakeCheck)
   178  				fakeCheck.IDReturns(1)
   179  
   180  				fakeEngine.NewCheckStub = func(build db.Check) engine.Runnable {
   181  					time.Sleep(time.Second)
   182  					return new(enginefakes.FakeRunnable)
   183  				}
   184  
   185  				fakeCheckFactory.StartedChecksReturns([]db.Check{
   186  					fakeCheck,
   187  					fakeCheck,
   188  				}, nil)
   189  
   190  				fakeLimiter.WaitReturns(nil)
   191  				fakeRateCalculator.RateLimiterReturns(fakeLimiter, nil)
   192  			})
   193  
   194  			It("succeeds", func() {
   195  				Expect(err).NotTo(HaveOccurred())
   196  			})
   197  
   198  			It("runs only one pending check", func() {
   199  				Eventually(fakeEngine.NewCheckCallCount).Should(Equal(1))
   200  			})
   201  		})
   202  
   203  		Context("when check run panicing", func() {
   204  			var fakeRunnable *enginefakes.FakeRunnable
   205  			var fakeCheck *dbfakes.FakeCheck
   206  
   207  			BeforeEach(func() {
   208  				fakeCheck = new(dbfakes.FakeCheck)
   209  				fakeRunnable = new(enginefakes.FakeRunnable)
   210  
   211  				fakeEngine.NewCheckReturns(fakeRunnable)
   212  
   213  				fakeCheckFactory.StartedChecksReturns([]db.Check{
   214  					fakeCheck,
   215  				}, nil)
   216  
   217  				fakeLimiter.WaitReturns(nil)
   218  				fakeRateCalculator.RateLimiterReturns(fakeLimiter, nil)
   219  
   220  				fakeRunnable.RunStub = func(context.Context) {
   221  					panic("something went wrong")
   222  				}
   223  			})
   224  
   225  			It("tries to run the runnable", func() {
   226  				Expect(err).NotTo(HaveOccurred())
   227  				Eventually(fakeRunnable.RunCallCount).Should(Equal(1))
   228  				Eventually(fakeCheck.FinishWithErrorCallCount).Should(Equal(1))
   229  				Eventually(fakeCheck.FinishWithErrorArgsForCall(0).Error).Should(ContainSubstring("something went wrong"))
   230  			})
   231  		})
   232  	})
   233  })