Files
kopia/internal/server/server_maintenance_test.go
Jarek Kowalski afb85cbb34 feat(cli): send error notifications and snapshot reports (#4233)
* feat(cli): send error notifications and snapshot reports

Notifications will be sent to all configured notification profiles
according to their severity levels.

The following events will trigger notifications:

- Snapshot is created (CLI only, severity >= report)
- Server Maintenance error occurs (CLI, server and UI, severity >= error)
- Any other CLI error occurs (CLI only, severity >= error).

A flag `--no-error-notifications` can be used to disable error notifications.

* added template tests

* improved time formatting in templates

* plumb through notifytemplate.Options

* more testing for formatting options

* fixed default date format to RFC1123
2024-11-11 17:53:50 -08:00

105 lines
2.4 KiB
Go

package server
import (
"context"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
"github.com/kopia/kopia/internal/clock"
"github.com/kopia/kopia/internal/repotesting"
"github.com/kopia/kopia/notification/notifytemplate"
"github.com/kopia/kopia/repo"
"github.com/kopia/kopia/repo/maintenance"
)
type testServer struct {
mu sync.Mutex
runCounter atomic.Int32
refreshSchedulerCount atomic.Int32
log func(msg string, args ...any)
// +checklocks:mu
err error
}
func (s *testServer) runMaintenanceTask(ctx context.Context, dr repo.DirectRepository) error {
s.runCounter.Add(1)
if s.log != nil {
s.log("runMaintenanceTask")
}
s.mu.Lock()
ne := s.err
s.err = nil
s.mu.Unlock()
return ne
}
func (s *testServer) refreshScheduler(reason string) {
s.refreshSchedulerCount.Add(1)
}
func (s *testServer) enableErrorNotifications() bool {
return false
}
func (s *testServer) notificationTemplateOptions() notifytemplate.Options {
return notifytemplate.DefaultOptions
}
func TestServerMaintenance(t *testing.T) {
ctx, env := repotesting.NewEnvironment(t, repotesting.FormatNotImportant)
require.NoError(t, repo.DirectWriteSession(ctx, env.RepositoryWriter, repo.WriteSessionOptions{}, func(ctx context.Context, dw repo.DirectRepositoryWriter) error {
return maintenance.SetParams(ctx, dw, &maintenance.Params{
Owner: env.Repository.ClientOptions().UsernameAtHost(),
QuickCycle: maintenance.CycleParams{
Enabled: true,
Interval: 5 * time.Second,
},
FullCycle: maintenance.CycleParams{
Enabled: true,
Interval: 10 * time.Second,
},
})
}))
ts := &testServer{log: t.Logf}
mm := startMaintenanceManager(ctx, env.RepositoryWriter, ts, time.Minute)
require.Equal(t, time.Time{}, mm.nextMaintenanceNoEarlierThan)
defer mm.stop(ctx)
// trigger and make sure it runs
mm.trigger()
require.Eventually(t, func() bool {
return ts.runCounter.Load() == 1 && ts.refreshSchedulerCount.Load() == 1
}, 3*time.Second, 10*time.Millisecond)
ts.mu.Lock()
ts.err = errors.New("some error")
ts.mu.Unlock()
mm.trigger()
require.Eventually(t, func() bool {
mm.mu.Lock()
defer mm.mu.Unlock()
return ts.runCounter.Load() == 2 && !mm.nextMaintenanceNoEarlierThan.IsZero()
}, 3*time.Second, 10*time.Millisecond)
// after a failure next maintenance time should be deferred by a minute.
require.Greater(t, mm.nextMaintenanceTime().Sub(clock.Now()), 50*time.Second)
}