resource/file/owner/helper_test.go
// Copyright © 2016 Asteris, LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package owner_test
import (
"os"
"os/user"
"path/filepath"
"strconv"
"syscall"
"testing"
"time"
"github.com/stretchr/testify/mock"
)
// any aliases mock.Anything for terseness
var any = mock.Anything
// MockOS mocks OSProxy
type MockOS struct {
mock.Mock
walkArgs []*walkArgs
}
// Walk mocks Walk
func (m *MockOS) Walk(root string, walkFunc filepath.WalkFunc) error {
args := m.Called(root, walkFunc)
for _, args := range m.walkArgs {
walkFunc(args.Path, args.Info, args.Err)
}
return args.Error(0)
}
// Stat mocks Stat
func (m *MockOS) Stat(path string) (os.FileInfo, error) {
args := m.Called(path)
return args.Get(0).(os.FileInfo), args.Error(1)
}
// Chown mocks Chown
func (m *MockOS) Chown(path string, uid, gid int) error {
args := m.Called(path, uid, gid)
return args.Error(0)
}
// GetUID mocks GetUID
func (m *MockOS) GetUID(path string) (int, error) {
args := m.Called(path)
return args.Int(0), args.Error(1)
}
// GetGID mocks GetGID
func (m *MockOS) GetGID(path string) (int, error) {
args := m.Called(path)
return args.Int(0), args.Error(1)
}
// LookupGroupID mocks LookupGroupID
func (m *MockOS) LookupGroupID(path string) (*user.Group, error) {
args := m.Called(path)
return args.Get(0).(*user.Group), args.Error(1)
}
// LookupGroup mocks LookupGroup
func (m *MockOS) LookupGroup(path string) (*user.Group, error) {
args := m.Called(path)
return args.Get(0).(*user.Group), args.Error(1)
}
// LookupID mocks LookupID
func (m *MockOS) LookupID(path string) (*user.User, error) {
args := m.Called(path)
return args.Get(0).(*user.User), args.Error(1)
}
// Lookup mocks Lookup
func (m *MockOS) Lookup(path string) (*user.User, error) {
args := m.Called(path)
return args.Get(0).(*user.User), args.Error(1)
}
func fakeUser(uid, gid, name string) *user.User {
return &user.User{
Uid: uid,
Gid: gid,
Username: name,
Name: name,
HomeDir: "~" + name,
}
}
func fakeGroup(gid, name string) *user.Group {
return &user.Group{Gid: gid, Name: name}
}
type ownershipRecord struct {
Path string
User *user.User
Group *user.Group
FileSize int64
FileMode os.FileMode
FileIsDir bool
}
func (o ownershipRecord) Name() string {
return o.Path
}
func (o ownershipRecord) Size() int64 {
return o.FileSize
}
func (o ownershipRecord) Mode() os.FileMode {
return o.FileMode
}
func (o ownershipRecord) ModTime() time.Time {
return time.Now()
}
func (o ownershipRecord) IsDir() bool {
return o.FileIsDir
}
func (o ownershipRecord) Sys() interface{} {
uid, _ := strconv.Atoi(o.User.Uid)
gid, _ := strconv.Atoi(o.User.Gid)
return &syscall.Stat_t{
Uid: uint32(uid),
Gid: uint32(gid),
Size: o.FileSize,
}
}
func (o *ownershipRecord) ToWalkArgs() *walkArgs {
return &walkArgs{
Path: o.Path,
Err: nil,
Info: o,
}
}
type walkArgs struct {
Path string
Info os.FileInfo
Err error
}
var (
rootUser = &user.User{
Uid: "0",
Gid: "0",
Username: "root",
Name: "root",
HomeDir: "/root",
}
rootGroup = &user.Group{
Gid: "0",
Name: "root",
}
fakeStat = &ownershipRecord{
Path: "fake-path",
User: rootUser,
Group: rootGroup,
FileSize: 0,
FileMode: 0777,
FileIsDir: false,
}
)
func makeOwned(path, username, uid, groupname, gid string) ownershipRecord {
return ownershipRecord{
Path: path,
User: &user.User{
Uid: uid,
Gid: gid,
Username: username,
Name: username,
HomeDir: "~" + username,
},
Group: &user.Group{
Gid: gid,
Name: groupname,
},
}
}
func makeOwnedFull(path, username, uid, groupname, gid string, isDir bool, size int64, mode os.FileMode) ownershipRecord {
o := makeOwned(path, username, uid, groupname, gid)
o.FileSize = size
o.FileMode = mode
o.FileIsDir = isDir
return o
}
func newMockOS(ownedFiles []ownershipRecord,
users []*user.User,
groups []*user.Group,
defaultUser *user.User,
defaultGroup *user.Group,
) *MockOS {
m := &MockOS{}
for _, o := range ownedFiles {
m.walkArgs = append(m.walkArgs, o.ToWalkArgs())
}
if defaultUser == nil {
defaultUser = rootUser
}
if defaultGroup == nil {
defaultGroup = rootGroup
}
m.On("Walk", any, any).Return(nil)
m.On("Chown", any, any, any).Return(nil)
for _, rec := range ownedFiles {
m.On("GetUID", rec.Path).Return(toInt(rec.User.Uid), nil)
m.On("GetGID", rec.Path).Return(toInt(rec.User.Gid), nil)
m.On("Stat", rec.Path).Return(rec, nil)
}
m.On("GetUID", any).Return(0, nil)
m.On("GetGID", any).Return(0, nil)
for _, user := range users {
m.On("LookupID", user.Uid).Return(user, nil)
m.On("Lookup", user.Username).Return(user, nil)
}
for _, group := range groups {
m.On("LookupGroupID", group.Gid).Return(group, nil)
m.On("LookupGroup", group.Name).Return(group, nil)
}
m.On("Lookup", any).Return(defaultUser, nil)
m.On("LookupID", any).Return(defaultUser, nil)
m.On("LookupGroup", any).Return(defaultGroup, nil)
m.On("LookupGroupID", any).Return(defaultGroup, nil)
m.On("Stat", any).Return(fakeStat, nil)
return m
}
func failingMockOS(failOn map[string]error) *MockOS {
m := &MockOS{}
m.On("Walk", any, any).Return(failOn["Walk"])
m.On("Chown", any, any, any).Return(failOn["Chown"])
m.On("GetUID", any).Return(0, failOn["GetUID"])
m.On("GetGID", any).Return(0, failOn["GetGID"])
if err, ok := failOn["Lookup"]; ok {
m.On("Lookup", any).Return(nil, err)
} else {
m.On("Lookup", any).Return(rootUser, nil)
}
if err, ok := failOn["LookupGroup"]; ok {
m.On("LookupGroup", any).Return(nil, err)
} else {
m.On("LookupGroup", any).Return(rootGroup, nil)
}
if err, ok := failOn["LookupID"]; ok {
m.On("LookupID", any).Return(nil, err)
} else {
m.On("LookupID", any).Return(rootUser, nil)
}
if err, ok := failOn["LookupGroupID"]; ok {
m.On("LookupGroupID", any).Return(nil, err)
} else {
m.On("LookupGroupID", any).Return(rootGroup, nil)
}
if err, ok := failOn["Stat"]; ok {
m.On("Stat", any).Return(fakeStat, err)
} else {
m.On("Stat", any).Return(fakeStat, nil)
}
return m
}
func missingMockOS() *MockOS {
return failingMockOS(map[string]error{"Stat": os.ErrNotExist})
}
func toInt(s string) int {
i, _ := strconv.Atoi(s)
return i
}
func assertIn(t *testing.T, strs []string, s string) {
for _, str := range strs {
if s == str {
return
}
}
t.Errorf("%s is not included in %v", s, strs)
}
func assertNotIn(t *testing.T, strs []string, s string) {
for _, str := range strs {
if s == str {
t.Errorf("%s should not be included in %v", s, strs)
}
}
return
}