blob: 921b98285ab96d35d83edcff53b8a786a716e78f [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package datastore
import (
"sort"
"testing"
"time"
"infra/appengine/rotang"
"golang.org/x/net/context"
"github.com/kylelemons/godebug/pretty"
"go.chromium.org/gae/service/datastore"
"go.chromium.org/luci/appengine/gaetesting"
)
func newTestContext() context.Context {
ctx := gaetesting.TestingContext()
testing := datastore.GetTestable(ctx)
testing.Consistent(true)
testing.AutoIndex(true)
return ctx
}
var locationUTC = func() *time.Location {
utcTZ, err := time.LoadLocation("UTC")
if err != nil {
panic(err)
}
return utcTZ
}()
func TestMemberOf(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
fail bool
ctx context.Context
email string
memberPool []rotang.Member
rotas []rotang.Configuration
want []string
}{{
name: "Failed context",
fail: true,
ctx: ctxCancel,
email: "doesntmatter@google.com",
memberPool: []rotang.Member{
{
Name: "Test Bot",
Email: "letestbot@google.com",
},
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
{
Name: "Another Test Sheriff",
Email: "anothersheriff@google.com",
},
},
rotas: []rotang.Configuration{
{
Config: rotang.Config{
Description: "Test description",
Name: "Sheriff Oncall Rotation",
Calendar: "testCalendarLink@testland.com",
ShiftsToSchedule: 4,
Email: rotang.Email{
Subject: "Chrome OS build sheriff reminder",
Body: "Some reminder",
},
Shifts: rotang.ShiftConfig{
ShiftMembers: 1,
},
},
Members: []rotang.ShiftMember{
{
Email: "letestbot@google.com",
},
{
Email: "testsheriff@google.com",
},
{
Email: "anothersheriff@google.com",
},
},
},
},
want: []string{"doesntmatter@google.com"},
}, {
name: "Member of single rota",
ctx: ctx,
email: "matters@google.com",
memberPool: []rotang.Member{
{
Name: "Test Bot",
Email: "matters@google.com",
},
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
{
Name: "Another Test Sheriff",
Email: "anothersheriff@google.com",
},
},
rotas: []rotang.Configuration{
{
Config: rotang.Config{
Description: "Test description",
Name: "Sheriff Oncall Rotation",
Calendar: "testCalendarLink@testland.com",
ShiftsToSchedule: 4,
Email: rotang.Email{
Subject: "Chrome OS build sheriff reminder",
Body: "Some reminder",
},
Shifts: rotang.ShiftConfig{
ShiftMembers: 1,
},
},
Members: []rotang.ShiftMember{
{
Email: "matters@google.com",
},
{
Email: "testsheriff@google.com",
},
{
Email: "anothersheriff@google.com",
},
},
},
},
want: []string{"Sheriff Oncall Rotation"},
}, {
name: "Member of multiple rotas",
ctx: ctx,
email: "matters@google.com",
memberPool: []rotang.Member{
{
Name: "Test Bot",
Email: "matters@google.com",
},
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
{
Name: "Another Test Sheriff",
Email: "anothersheriff@google.com",
},
},
rotas: []rotang.Configuration{
{
Config: rotang.Config{
Description: "Test description",
Name: "Sheriff Oncall Rotation",
Calendar: "testCalendarLink@testland.com",
ShiftsToSchedule: 4,
Email: rotang.Email{
Subject: "Chrome OS build sheriff reminder",
Body: "Some reminder",
},
Shifts: rotang.ShiftConfig{
ShiftMembers: 1,
},
},
Members: []rotang.ShiftMember{
{
Email: "matters@google.com",
},
{
Email: "testsheriff@google.com",
},
{
Email: "anothersheriff@google.com",
},
},
}, {
Config: rotang.Config{
Description: "Test description",
Name: "Test Rota",
Calendar: "testCalendarLink@testland.com",
ShiftsToSchedule: 4,
Email: rotang.Email{
Subject: "Chrome OS build sheriff reminder",
Body: "Some reminder",
},
Shifts: rotang.ShiftConfig{
ShiftMembers: 1,
},
},
Members: []rotang.ShiftMember{
{
Email: "matters@google.com",
},
{
Email: "testsheriff@google.com",
},
{
Email: "anothersheriff@google.com",
},
},
},
},
want: []string{"Sheriff Oncall Rotation", "Test Rota"},
}, {
name: "Not a member of any rotas",
ctx: ctx,
email: "matters@google.com",
memberPool: []rotang.Member{
{
Name: "Test Bot",
Email: "matters@google.com",
},
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
{
Name: "Another Test Sheriff",
Email: "anothersheriff@google.com",
},
},
rotas: []rotang.Configuration{
{
Config: rotang.Config{
Description: "Test description",
Name: "Sheriff Oncall Rotation",
Calendar: "testCalendarLink@testland.com",
ShiftsToSchedule: 4,
Email: rotang.Email{
Subject: "Chrome OS build sheriff reminder",
Body: "Some reminder",
},
Shifts: rotang.ShiftConfig{
ShiftMembers: 1,
},
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
{
Email: "anothersheriff@google.com",
},
},
}, {
Config: rotang.Config{
Description: "Test description",
Name: "Test Rota",
Calendar: "testCalendarLink@testland.com",
ShiftsToSchedule: 4,
Email: rotang.Email{
Subject: "Chrome OS build sheriff reminder",
Body: "Some reminder",
},
Shifts: rotang.ShiftConfig{
ShiftMembers: 1,
},
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
{
Email: "anothersheriff@google.com",
},
},
},
},
}, {
name: "Member of some rotas",
ctx: ctx,
email: "matters@google.com",
memberPool: []rotang.Member{
{
Name: "Test Bot",
Email: "matters@google.com",
},
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
{
Name: "Another Test Sheriff",
Email: "anothersheriff@google.com",
},
},
rotas: []rotang.Configuration{
{
Config: rotang.Config{
Description: "Test description",
Name: "Sheriff Oncall Rotation",
Calendar: "testCalendarLink@testland.com",
ShiftsToSchedule: 4,
Email: rotang.Email{
Subject: "Chrome OS build sheriff reminder",
Body: "Some reminder",
},
Shifts: rotang.ShiftConfig{
ShiftMembers: 1,
},
},
Members: []rotang.ShiftMember{
{
Email: "matters@google.com",
},
{
Email: "testsheriff@google.com",
},
{
Email: "anothersheriff@google.com",
},
},
}, {
Config: rotang.Config{
Description: "Test description",
Name: "Test Rota",
Calendar: "testCalendarLink@testland.com",
ShiftsToSchedule: 4,
Email: rotang.Email{
Subject: "Chrome OS build sheriff reminder",
Body: "Some reminder",
},
Shifts: rotang.ShiftConfig{
ShiftMembers: 1,
},
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
{
Email: "anothersheriff@google.com",
},
},
}, {
Config: rotang.Config{
Description: "Test description",
Name: "Second Test Rota",
Calendar: "testCalendarLink@testland.com",
ShiftsToSchedule: 4,
Email: rotang.Email{
Subject: "Chrome OS build sheriff reminder",
Body: "Some reminder",
},
Shifts: rotang.ShiftConfig{
ShiftMembers: 1,
},
},
Members: []rotang.ShiftMember{
{
Email: "matters@google.com",
},
{
Email: "testsheriff@google.com",
},
{
Email: "anothersheriff@google.com",
},
},
},
},
want: []string{"Sheriff Oncall Rotation", "Second Test Rota"},
},
}
s := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, m := range tst.memberPool {
if err := s.CreateMember(ctx, &m); err != nil {
t.Fatalf("%s: s.CreateMember(_, _) failed: %v", tst.name, err)
}
defer s.DeleteMember(ctx, m.Email)
}
for _, cfg := range tst.rotas {
if err := s.CreateRotaConfig(ctx, &cfg); err != nil {
t.Fatalf("%s: s.CreateRotaConfig(_, _) failed: %v", tst.name, err)
}
defer s.DeleteRotaConfig(ctx, cfg.Config.Name)
}
res, err := s.MemberOf(tst.ctx, tst.email)
if got, want := (err != nil), tst.fail; got != want {
t.Fatalf("%s: s.MemberOf(_, %q) = %t want: %t, err: %v", tst.name, tst.email, got, want, err)
}
if err != nil {
return
}
sort.Strings(tst.want)
sort.Strings(res)
if diff := pretty.Compare(tst.want, res); diff != "" {
t.Fatalf("%s: s.MemberOf(_, %q) differs -want +got: %s", tst.name, tst.email, diff)
}
})
}
}
func TestChangeRotaState(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
s := New(ctx)
tests := []struct {
name string
fail bool
rota string
ctx context.Context
cfg *rotang.Configuration
testFunc func(context.Context, string) error
want bool
}{{
name: "Enable Success",
ctx: ctx,
testFunc: s.EnableRota,
rota: "Test Rota",
cfg: &rotang.Configuration{
Config: rotang.Config{
Name: "Test Rota",
Enabled: false,
},
},
want: true,
}, {
name: "Canceled Context",
fail: true,
ctx: ctxCancel,
testFunc: s.EnableRota,
cfg: &rotang.Configuration{
Config: rotang.Config{
Name: "Test Rota",
Enabled: false,
},
},
}, {
name: "Disable success",
ctx: ctx,
rota: "Test Rota",
testFunc: s.DisableRota,
cfg: &rotang.Configuration{
Config: rotang.Config{
Name: "Test Rota",
},
},
},
}
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
if err := s.CreateRotaConfig(ctx, tst.cfg); err != nil {
t.Fatalf("%s: CreateRotaConfig(ctx, _) failed: %v", tst.name, err)
}
defer s.DeleteRotaConfig(ctx, tst.cfg.Config.Name)
err := tst.testFunc(tst.ctx, tst.rota)
if got, want := (err != nil), tst.fail; got != want {
t.Fatalf("%s: tstFunc(ctx, %q) = %t, want: %t, err: %v", tst.name, tst.rota, got, want, err)
}
if err != nil {
return
}
got, err := s.RotaEnabled(ctx, tst.rota)
if err != nil {
t.Fatalf("%s: RotaEnabled(ctx, %q) failed: %v", tst.name, tst.rota, err)
}
if got != tst.want {
t.Fatalf("%s: RotaEnabled(ctx, %q) = %t want: %t", tst.name, tst.rota, got, tst.want)
}
})
}
}
func TestMember(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
ctx context.Context
fail bool
email string
store []rotang.Member
want rotang.Member
}{{
name: "Canceled context",
fail: true,
email: "notmatter@test.se",
ctx: ctxCancel,
store: []rotang.Member{
{
Email: "notmatter@test.se",
},
},
}, {
name: "Success fetching member",
ctx: ctx,
email: "oncall@dot.com",
store: []rotang.Member{
{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
want: rotang.Member{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
}, {
name: "Member not found",
fail: true,
ctx: ctx,
email: "notfound@dot.com",
store: []rotang.Member{
{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
want: rotang.Member{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
}, {
name: "Empty eMail",
fail: true,
ctx: ctx,
store: []rotang.Member{
{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
},
}
store := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, sm := range tst.store {
if err := store.CreateMember(ctx, &sm); err != nil {
t.Fatalf("%s: store.CreateMember(_, _) failed: %v", tst.name, err)
}
defer func(email string) {
if err := store.DeleteMember(ctx, email); err != nil {
t.Logf("%s: store.DeleteMember(ctx, %q) failed: %v", tst.name, email, err)
}
}(sm.Email)
}
m, err := store.Member(tst.ctx, tst.email)
if got, want := (err != nil), tst.fail; got != want {
t.Fatalf("%s: store.Member(_, %q) = %t want: %t, err: %v", tst.name, tst.email, got, want, err)
}
if err != nil {
return
}
if diff := pretty.Compare(tst.want, m); diff != "" {
t.Fatalf("%s: store.Member(_, %q) differs -want +got: %s", tst.name, tst.email, diff)
}
})
}
}
func TestUpdateMember(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
fail bool
ctx context.Context
store []rotang.Member
update *rotang.Member
}{{
name: "Canceled context",
fail: true,
ctx: ctxCancel,
}, {
name: "Update sucess",
ctx: ctx,
store: []rotang.Member{
{
Name: "Before update",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
update: &rotang.Member{
Name: "After update",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
}, {
name: "No email",
fail: true,
ctx: ctx,
store: []rotang.Member{
{
Name: "Before update",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
update: &rotang.Member{
Name: "After update",
TZ: *locationUTC,
},
}, {
name: "Member not exist",
fail: true,
ctx: ctx,
store: []rotang.Member{
{
Name: "Before update",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
update: &rotang.Member{
Name: "After update",
Email: "not-exist@dot.com",
TZ: *locationUTC,
},
}}
store := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, sm := range tst.store {
if err := store.CreateMember(ctx, &sm); err != nil {
t.Fatalf("%s: store.CreateMember(_, _) failed: %v", tst.name, err)
}
defer func(email string) {
if err := store.DeleteMember(ctx, email); err != nil {
t.Logf("%s: store.DeleteMember(ctx, %q) failed: %v", tst.name, email, err)
}
}(sm.Email)
}
err := store.UpdateMember(tst.ctx, tst.update)
if got, want := (err != nil), tst.fail; got != want {
t.Fatalf("%s: store.UpdateMember(_, _) = %t want: %t, err: %v", tst.name, got, want, err)
}
if err != nil {
return
}
m, err := store.Member(ctx, tst.update.Email)
if err != nil {
t.Fatalf("%s: store.Member(_, %q) failed: %v", tst.name, tst.update.Email, err)
}
if diff := pretty.Compare(tst.update, m); diff != "" {
t.Fatalf("%s: store.Member(_, %q) differ -want +got: %s", tst.name, tst.update.Email, diff)
}
})
}
}
func TestAllMembers(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
fail bool
ctx context.Context
memberPool []rotang.Member
}{{
name: "Canceled context",
fail: true,
ctx: ctxCancel,
memberPool: []rotang.Member{
{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
}, {
name: "AllMembers Success",
ctx: ctx,
memberPool: []rotang.Member{
{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
}, {
Name: "Secondary Oncaller",
Email: "secondary@dot.com",
TZ: *locationUTC,
},
},
}, {
name: "AllMembers No Members in the pool",
ctx: ctx,
fail: true,
},
}
store := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, m := range tst.memberPool {
if err := store.CreateMember(ctx, &m); err != nil {
t.Fatalf("%s: store.CreateMember(_, _) failed: %v", tst.name, err)
}
defer func(email string) {
if err := store.DeleteMember(ctx, email); err != nil {
t.Logf("%s: store.DeleteMember(ctx, %q) failed: %v", tst.name, email, err)
}
}(m.Email)
}
ms, err := store.AllMembers(tst.ctx)
t.Logf("%s: Members: %v", tst.name, ms)
if got, want := (err != nil), tst.fail; got != want {
t.Fatalf("%s: store.AllMembers(ctx) = %t want: %t, err: %v", tst.name, got, want, err)
}
if err != nil {
return
}
if diff := pretty.Compare(tst.memberPool, ms); diff != "" {
t.Fatalf("%s: store.AllMembers(ctx) differ -want +got: %s", tst.name, diff)
}
})
}
}
func TestDeleteMember(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
fail bool
ctx context.Context
email string
store []rotang.Member
}{{
name: "Canceled context",
fail: true,
email: "a@b.com",
ctx: ctxCancel,
store: []rotang.Member{
{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
}, {
name: "Delete success",
email: "oncall@dot.com",
ctx: ctx,
store: []rotang.Member{
{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
}, {
name: "Delete not-exist",
email: "noexist@dot.com",
ctx: ctx,
store: []rotang.Member{
{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
},
}
store := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, sm := range tst.store {
if err := store.CreateMember(ctx, &sm); err != nil {
t.Fatalf("%s: store.CreateMember(_, _) failed: %v", tst.name, err)
}
defer func(email string) {
if err := store.DeleteMember(ctx, email); err != nil {
t.Logf("%s: store.DeleteMember(ctx, %q) failed: %v", tst.name, email, err)
}
}(sm.Email)
}
err := store.DeleteMember(tst.ctx, tst.email)
if got, want := (err != nil), tst.fail; got != want {
t.Fatalf("%s: store.DeleteMember(_, %q) = %t want: %t, err: %v", tst.name, tst.email, got, want, err)
}
})
}
}
func TestCreateMember(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
ctx context.Context
fail bool
email string
store []rotang.Member
create *rotang.Member
}{{
name: "Canceled context",
fail: true,
email: "notmatter@test.se",
ctx: ctxCancel,
create: &rotang.Member{
Email: "notmatter@test.se",
},
}, {
name: "Success creating member",
ctx: ctx,
email: "oncall@dot.com",
create: &rotang.Member{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
}, {
name: "Empty eMail",
fail: true,
ctx: ctx,
create: &rotang.Member{
Name: "Primary Oncaller",
TZ: *locationUTC,
},
}, {
name: "Already existing member",
fail: true,
ctx: ctx,
email: "oncall@dot.com",
store: []rotang.Member{
{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
create: &rotang.Member{
Name: "Primary Oncaller",
Email: "oncall@dot.com",
TZ: *locationUTC,
},
},
}
store := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, sm := range tst.store {
if err := store.CreateMember(ctx, &sm); err != nil {
t.Fatalf("%s: store.CreateMember(_, _) failed: %v", tst.name, err)
}
defer func(email string) {
if err := store.DeleteMember(ctx, email); err != nil {
t.Logf("%s: store.DeleteMember(ctx, %q) failed: %v", tst.name, email, err)
}
}(sm.Email)
}
err := store.CreateMember(tst.ctx, tst.create)
if got, want := (err != nil), tst.fail; got != want {
t.Fatalf("%s: store.CreateMember(_, _) = %t want: %t, err: %v", tst.name, got, want, err)
}
if err != nil {
return
}
defer func() {
if err := store.DeleteMember(ctx, tst.email); err != nil {
t.Logf("%s: store.DeleteMember(ctx, sm.Email) failed: %v", tst.name, err)
}
}()
m, err := store.Member(ctx, tst.email)
if err != nil {
t.Fatalf("%s: store.Member(_, %q) failed: %v", tst.name, tst.email, err)
}
if err != nil {
return
}
if diff := pretty.Compare(tst.create, m); diff != "" {
t.Fatalf("%s: store.Member(_, %q) differs -want +got: %s", tst.name, tst.email, diff)
}
})
}
}
func TestCreateRotaConfiguration(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
ctx context.Context
fail bool
in rotang.Configuration
memberPool []rotang.Member
}{
{
name: "Store success",
ctx: ctx,
in: rotang.Configuration{
Config: rotang.Config{
Description: "Test description",
Name: "Sheriff Oncall Rotation",
Calendar: "testCalendarLink@testland.com",
ShiftsToSchedule: 4,
Email: rotang.Email{
Subject: "Chrome OS build sheriff reminder",
Body: "Some reminder",
},
Shifts: rotang.ShiftConfig{
ShiftMembers: 1,
},
},
Members: []rotang.ShiftMember{
{
Email: "letestbot@google.com",
},
{
Email: "testsheriff@google.com",
},
{
Email: "anothersheriff@google.com",
},
},
},
memberPool: []rotang.Member{
{
Name: "Test Bot",
Email: "letestbot@google.com",
},
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
{
Name: "Another Test Sheriff",
Email: "anothersheriff@google.com",
},
},
}, {
name: "Store invalid context",
ctx: ctxCancel,
in: rotang.Configuration{
Config: rotang.Config{
Name: "bleh",
},
},
fail: true,
},
}
store := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, m := range tst.memberPool {
if err := store.CreateMember(ctx, &m); err != nil {
t.Fatalf("%s: store.CreateMember(_, _) failed: %v", tst.name, err)
}
defer store.DeleteMember(ctx, m.Email)
}
err := store.CreateRotaConfig(tst.ctx, &tst.in)
if got, want := (err != nil), tst.fail; got != want {
t.Fatalf("%s: datastore.CreateRotaConfig() = %t want: %t, err: %v", tst.name, got, want, err)
}
if err != nil {
return
}
got, err := store.RotaConfig(ctx, tst.in.Config.Name)
if err != nil {
t.Fatalf("%s: store.RotaConfig(ctx, %q) failed: %v", tst.name, tst.in.Config.Name, err)
}
sort.Slice(tst.in.Members, func(i, j int) bool {
return tst.in.Members[i].Email < tst.in.Members[j].Email
})
sort.Slice(got[0].Members, func(i, j int) bool {
return got[0].Members[i].Email < got[0].Members[j].Email
})
if diff := pretty.Compare(tst.in, got[0]); diff != "" {
t.Errorf("%s: store.RotaConfig(ctx, \"Chrome OS Build Sheriff\") differs -want +got: %v", tst.name, diff)
}
})
}
}
func TestUpdateRotaConfig(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
fail bool
ctx context.Context
memberPool []rotang.Member
put []rotang.Configuration
update *rotang.Configuration
}{
{
name: "Canceled context",
fail: true,
ctx: ctxCancel,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "test update",
},
},
},
update: &rotang.Configuration{
Config: rotang.Config{
Name: "test update",
Description: "Updatet desc",
},
},
}, {
name: "Simple working",
ctx: ctx,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "test update",
},
},
},
update: &rotang.Configuration{
Config: rotang.Config{
Name: "test update",
Description: "Updatet desc",
Shifts: rotang.ShiftConfig{},
},
},
}, {
name: "Configuration don't exist",
ctx: ctx,
fail: true,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "Something different",
},
},
},
update: &rotang.Configuration{
Config: rotang.Config{
Name: "test update",
Description: "Updatet desc",
Shifts: rotang.ShiftConfig{},
},
},
},
}
s := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, m := range tst.memberPool {
if err := s.CreateMember(ctx, &m); err != nil {
t.Fatalf("%s: store.CreateMember(_, _) failed: %v", tst.name, err)
}
defer s.DeleteMember(ctx, m.Email)
}
for _, cfg := range tst.put {
if err := s.CreateRotaConfig(ctx, &cfg); err != nil {
t.Fatalf("%s: s.StoreRotaConfig(ctx, _) failed: %v", tst.name, err)
}
defer s.DeleteRotaConfig(ctx, cfg.Config.Name)
}
err := s.UpdateRotaConfig(tst.ctx, tst.update)
if got, want := (err != nil), tst.fail; got != want {
t.Fatalf("%s: s.UpdateRotaConfig(_, _) = %t want: %t, err: %v", tst.name, got, want, err)
}
if err != nil {
return
}
res, err := s.RotaConfig(ctx, tst.update.Config.Name)
if err != nil {
t.Fatalf("%s: s.RotaConfig(_, %q) failed: %v", tst.name, tst.update.Config.Name, err)
}
if diff := pretty.Compare(tst.update, res[0]); diff != "" {
t.Fatalf("%s: s.UpdateRotaConfig(_, _) differs -want +got: %s", tst.name, diff)
}
})
}
}
func TestFetchConfiguration(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
fail bool
ctx context.Context
memberPool []rotang.Member
put []rotang.Configuration
get string
want []rotang.Configuration
}{
{
name: "Single Fetch",
ctx: ctx,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
get: "test fetch",
want: []rotang.Configuration{
{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
},
{
name: "Fetch non exist",
fail: true,
ctx: ctx,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
get: "test non exist",
want: []rotang.Configuration{
{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
},
{
name: "Fetch cancelled ctx",
fail: true,
ctx: ctxCancel,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
get: "test fetch",
},
{
name: "Fetch multiple",
ctx: ctx,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "ChromeOS Sheriff",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
{
Config: rotang.Config{
Name: "Chromium Sheriff",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
want: []rotang.Configuration{
{
Config: rotang.Config{
Name: "ChromeOS Sheriff",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
{
Config: rotang.Config{
Name: "Chromium Sheriff",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
},
{
name: "Rota does not exist",
fail: true,
ctx: ctx,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "ChromeOS Sheriff",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
get: "non-exist",
},
{
name: "Fetch all no rotas",
fail: true,
ctx: ctx,
},
}
s := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, m := range tst.memberPool {
if err := s.CreateMember(ctx, &m); err != nil {
t.Fatalf("%s: s.CreateMember(_, _) failed: %v", tst.name, err)
}
defer s.DeleteMember(ctx, m.Email)
}
for _, cfg := range tst.put {
if err := s.CreateRotaConfig(ctx, &cfg); err != nil {
t.Fatalf("%s: s.StoreRotaConfig(ctx,_) failed: %v", tst.name, err)
}
defer datastore.Delete(ctx, &DsRotaConfig{
Key: rootKey(ctx),
ID: cfg.Config.Name,
})
}
got, err := s.RotaConfig(tst.ctx, tst.get)
if got, want := (err != nil), tst.fail; got != want {
t.Errorf("%s: s.FetchRotaConfig(ctx,%q) = %t want: %t, err: %v", tst.name, tst.get, got, want, err)
return
}
if err != nil {
return
}
sort.Slice(got, func(i, j int) bool {
return got[i].Config.Name < got[j].Config.Name
})
sort.Slice(tst.put, func(i, j int) bool {
return tst.put[i].Config.Name < tst.put[j].Config.Name
})
if diff := pretty.Compare(tst.want, got); diff != "" {
t.Errorf("%s: s.FetchRota(ctx, %q) differs -want +got: %s", tst.name, tst.get, diff)
}
})
}
}
func TestDeleteConfiguration(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
fail bool
memberPool []rotang.Member
ctx context.Context
put []rotang.Configuration
in string
}{
{
name: "Delete success",
ctx: ctx,
memberPool: []rotang.Member{
{
Name: "Test Bot",
Email: "letestbot@google.com",
},
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
{
Name: "Another Test Sheriff",
Email: "anothersheriff@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
in: "test fetch",
},
{
name: "Cancelled context",
ctx: ctxCancel,
fail: true,
memberPool: []rotang.Member{
{
Name: "Test Bot",
Email: "letestbot@google.com",
},
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
{
Name: "Another Test Sheriff",
Email: "anothersheriff@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
in: "test fetch",
},
}
s := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, m := range tst.memberPool {
if err := s.CreateMember(ctx, &m); err != nil {
t.Fatalf("%s: store.CreateMember(_, _) failed: %v", tst.name, err)
}
defer s.DeleteMember(ctx, m.Email)
}
for _, cfg := range tst.put {
if err := s.CreateRotaConfig(ctx, &cfg); err != nil {
t.Fatalf("%s: s.StoraRotaConfig(ctx,_) failed: %v", tst.name, err)
}
defer datastore.Delete(ctx, &DsRotaConfig{
ID: cfg.Config.Name,
})
}
if got, want := s.DeleteRotaConfig(tst.ctx, tst.in) != nil, tst.fail; got != want {
t.Errorf("%s: s.DeleteRotaConfig(ctx, %q) = %t want: %t, err: %v", tst.name, tst.in, got, want, got)
return
}
})
}
}
func TestAddMember(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
fail bool
memberPool []rotang.Member
ctx context.Context
put rotang.Configuration
rota string
in rotang.ShiftMember
want []rotang.ShiftMember
}{
{
name: "AddMember success",
ctx: ctx,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
{
Name: "Another Test Sheriff",
Email: "brandnew@google.com",
},
},
rota: "test fetch",
put: rotang.Configuration{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
in: rotang.ShiftMember{
Email: "brandnew@google.com",
},
want: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
}, {
Email: "brandnew@google.com",
},
},
},
{
name: "Expired context",
ctx: ctxCancel,
memberPool: []rotang.Member{
{
Name: "Another Test Sheriff",
Email: "brandnew@google.com",
},
},
rota: "Dont matter",
fail: true,
in: rotang.ShiftMember{
Email: "brandnew@google.com",
},
put: rotang.Configuration{
Config: rotang.Config{
Name: "Dont matter",
},
Members: []rotang.ShiftMember{
{
Email: "brandnew@google.com",
},
},
},
},
{
name: "Rota no exist",
ctx: ctx,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
fail: true,
rota: "Dont exist",
put: rotang.Configuration{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
{
name: "Existing member",
fail: true,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
ctx: ctx,
rota: "test fetch",
put: rotang.Configuration{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
in: rotang.ShiftMember{
Email: "testsheriff@google.com",
},
want: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
}
s := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, m := range tst.memberPool {
if err := s.CreateMember(ctx, &m); err != nil {
t.Fatalf("%s: store.CreateMember(_, _) failed: %v", tst.name, err)
}
defer s.DeleteMember(ctx, m.Email)
}
if err := s.CreateRotaConfig(ctx, &tst.put); err != nil {
t.Fatalf("%s: s.CreateRotaConfig(ctx,_) failed: %v", tst.name, err)
}
defer s.DeleteRotaConfig(ctx, tst.put.Config.Name)
err := s.AddRotaMember(tst.ctx, tst.rota, &tst.in)
if got, want := (err != nil), tst.fail; got != want {
t.Fatalf("%s: s.AddRotaMember(ctx, %q, _) = %t want: %t, err: %v", tst.name, tst.rota, got, want, err)
}
if err != nil {
return
}
resRota, err := s.RotaConfig(ctx, tst.put.Config.Name)
if err != nil {
t.Fatalf("%s: s.RotaConfig(ctx, %q) failed: %v", tst.name, tst.put.Config.Name, err)
}
if len(resRota) != 1 {
t.Fatalf("%s: s.RotaConfig(ctx, %q) = %d want: %d, number of results differ", tst.name, tst.put.Config.Name, len(resRota), -1)
}
sort.Slice(resRota[0].Members, func(i, j int) bool {
return resRota[0].Members[i].Email < resRota[0].Members[j].Email
})
sort.Slice(tst.want, func(i, j int) bool {
return tst.want[i].Email < tst.want[j].Email
})
if diff := pretty.Compare(tst.want, resRota[0].Members); diff != "" {
t.Fatalf("%s: s.AddRotaMember(ctx, %q, _) differs -want +got: %s", tst.name, tst.rota, diff)
}
})
}
}
func TestDeleteRotaMember(t *testing.T) {
ctx := newTestContext()
ctxCancel, cancel := context.WithCancel(ctx)
cancel()
tests := []struct {
name string
fail bool
memberPool []rotang.Member
ctx context.Context
put []rotang.Configuration
rota string
email string
want []rotang.ShiftMember
}{
{
name: "Delete Success",
ctx: ctx,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
rota: "test fetch",
email: "testsheriff@google.com",
},
{
name: "Expired Context",
ctx: ctxCancel,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
},
fail: true,
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "test fetch",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
},
},
},
rota: "test fetch",
email: "testsheriff@google.com",
},
{
name: "Delete with multiple rotas and members",
ctx: ctx,
memberPool: []rotang.Member{
{
Name: "Test Sheriff",
Email: "testsheriff@google.com",
},
{
Name: "Test Sheriff2",
Email: "testsheriff2@google.com",
},
{
Name: "Test Sheriff3",
Email: "testsheriff3@google.com",
},
},
put: []rotang.Configuration{
{
Config: rotang.Config{
Name: "rota-one",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
{
Email: "testsheriff2@google.com",
},
{
Email: "testsheriff3@google.com",
},
},
},
{
Config: rotang.Config{
Name: "rota-two",
},
Members: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
{
Email: "testsheriff2@google.com",
},
{
Email: "testsheriff3@google.com",
},
},
},
},
rota: "rota-one",
email: "testsheriff2@google.com",
want: []rotang.ShiftMember{
{
Email: "testsheriff@google.com",
},
{
Email: "testsheriff3@google.com",
},
},
},
}
s := New(ctx)
for _, tst := range tests {
t.Run(tst.name, func(t *testing.T) {
for _, m := range tst.memberPool {
if err := s.CreateMember(ctx, &m); err != nil {
t.Fatalf("%s: store.CreateMember(_, _) failed: %v", tst.name, err)
}
defer s.DeleteMember(ctx, m.Email)
}
for _, cfg := range tst.put {
if err := s.CreateRotaConfig(ctx, &cfg); err != nil {
t.Fatalf("%s: s.StoraRotaConfig(ctx,_) failed: %v", tst.name, err)
}
defer s.DeleteRotaConfig(ctx, cfg.Config.Name)
}
err := s.DeleteRotaMember(tst.ctx, tst.rota, tst.email)
if got, want := (err != nil), tst.fail; got != want {
t.Fatalf("%s: s.DeleteRotaMember(ctx, %q, %q) = %t want: %t, err: %v", tst.name, tst.rota, tst.email, got, want, err)
}
if err != nil {
return
}
resRota, err := s.RotaConfig(ctx, tst.rota)
if err != nil {
t.Fatalf("%s: s.FetchRotaConfig(ctx, %q) failed: %v", tst.name, tst.rota, err)
}
if len(resRota) != 1 {
t.Fatalf("%s: s.FetchRotaConfig(ctx, %q) = %d want: %d, number of results differ", tst.name, tst.rota, len(resRota), -1)
}
sort.Slice(resRota[0].Members, func(i, j int) bool {
return resRota[0].Members[i].Email < resRota[0].Members[j].Email
})
sort.Slice(tst.want, func(i, j int) bool {
return tst.want[i].Email < tst.want[j].Email
})
if diff := pretty.Compare(tst.want, resRota[0].Members); diff != "" {
t.Fatalf("%s: s.DeleteRotaMember(ctx, %q, %s) differs -want +got: %s", tst.name, tst.rota, tst.email, diff)
}
})
}
}