I'm trying to write tests for this function in Go:
type LinuxBackend struct {
fs afero.Fs
}
func (b *LinuxBackend) GetSSHUser(name string) users.SSHUser {
// Get the user from the system
usr, err := user.Lookup(name)
if err != nil {
panic(err)
}
return users.SSHUser{ User: usr }
}
I'd like to have a fake /etc/passwd
so that my tests can create the necessary users in that file instead of relying on the real system file.
For other parts of my code, I abstracted the underlying file system using Afero to create an in-memory file system, creating the necessary files (including /etc/passwd
) so the tests can run. That's what the fs
field in LinuxBackend
is for.
For example, here's another function from the same package:
func (b *LinuxBackend) GetMinMaxUid() (int, int) {
content, err := afero.ReadFile(b.fs, LoginDefsPath)
// more code here...
return min, max
}
I can then test this without touching the real file system:
func createLoginDefs(afs afero.Fs) {
afs.MkdirAll("/etc", os.FileMode(os.ModeDir))
f, _ := afs.OpenFile(LoginDefsPath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
defer f.Close()
f.WriteString("UID_MIN " + strconv.Itoa(uidMin) + "
")
f.WriteString("UID_MAX " + strconv.Itoa(uidMax) + "
")
}
func TestLinuxBackend_GetMinMaxUid(t *testing.T) {
afs := afero.NewMemMapFs() // <-- This is the trick.
createLoginDefs(afs)
backend := LinuxBackend{afs}
min, max := backend.GetMinMaxUid()
// rest of the test...
}
I can't do that for user.Lookup()
, though, which means it will try to fetch the non-existent test users from the real file system.
user.Lookup
calls user.lookupUser
, which has two concrete implementations, a pure Go one, and a cgo one. The latter is completely impossible to coerce to use a virtual file system, I believe, since it uses libc's pw*
utility functions to fetch users. The former starts by Open
ing a file handle and then passes it to a findUsername
function that takes an io.Reader
as an argument, but this function is not exported from the module, so I can't use it directly to pass a Reader from the Afero file system to it.
Is there a way I can somehow make Go use the in-memory file system for user.Lookup
and user.lookupUser
, or write the code or test in some other way so that user.Lookup
doesn't use the real file system?
I could always copy user.lookupUser
but I'd rather avoid that.