mirror of
https://blitiri.com.ar/repos/chasquid
synced 2025-12-18 14:47:03 +00:00
We have many places in our tests where we create temporary directories, which we later remove (most of the time). We have at least 3 helpers to do this, and various places where it's done ad-hoc (and the cleanup is not always present). To try to reduce the clutter, and make the tests more uniform and readable, this patch introduces two helpers in a new "testutil" package: one for creating and one for removing temporary directories. These new functions are safer, better tested, and make the tests more consistent. All the tests are updated to use them.
322 lines
7.4 KiB
Go
322 lines
7.4 KiB
Go
package userdb
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
// Remove the file if the test was successful. Used in defer statements, to
|
|
// leave files around for inspection when the tests failed.
|
|
func removeIfSuccessful(t *testing.T, fname string) {
|
|
if !t.Failed() {
|
|
os.Remove(fname)
|
|
}
|
|
}
|
|
|
|
// Create a database with the given content on a temporary filename. Return
|
|
// the filename, or an error if there were errors creating it.
|
|
func mustCreateDB(t *testing.T, content string) string {
|
|
f, err := ioutil.TempFile("", "userdb_test")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err := f.WriteString(content); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Logf("file: %q", f.Name())
|
|
return f.Name()
|
|
}
|
|
|
|
func dbEquals(a, b *DB) bool {
|
|
if a.db == nil || b.db == nil {
|
|
return a.db == nil && b.db == nil
|
|
}
|
|
|
|
if len(a.db.Users) != len(b.db.Users) {
|
|
return false
|
|
}
|
|
|
|
for k, av := range a.db.Users {
|
|
bv, ok := b.db.Users[k]
|
|
if !ok || !reflect.DeepEqual(av, bv) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
var emptyDB = &DB{
|
|
db: &ProtoDB{Users: map[string]*Password{}},
|
|
}
|
|
|
|
// Test various cases of loading an empty/broken database.
|
|
func TestEmptyLoad(t *testing.T) {
|
|
cases := []struct {
|
|
desc string
|
|
content string
|
|
fatal bool
|
|
fatalErr error
|
|
}{
|
|
{"empty file", "", false, nil},
|
|
{"invalid ", "users: < invalid >", true, nil},
|
|
}
|
|
|
|
for _, c := range cases {
|
|
testOneLoad(t, c.desc, c.content, c.fatal, c.fatalErr)
|
|
}
|
|
}
|
|
|
|
func testOneLoad(t *testing.T, desc, content string, fatal bool, fatalErr error) {
|
|
fname := mustCreateDB(t, content)
|
|
defer removeIfSuccessful(t, fname)
|
|
db, err := Load(fname)
|
|
if fatal {
|
|
if err == nil {
|
|
t.Errorf("case %q: expected error loading, got nil", desc)
|
|
}
|
|
if fatalErr != nil && fatalErr != err {
|
|
t.Errorf("case %q: expected error %v, got %v", desc, fatalErr, err)
|
|
}
|
|
} else if !fatal && err != nil {
|
|
t.Fatalf("case %q: error loading database: %v", desc, err)
|
|
}
|
|
|
|
if db != nil && !dbEquals(db, emptyDB) {
|
|
t.Errorf("case %q: DB not empty: %#v", desc, db.db.Users)
|
|
}
|
|
}
|
|
|
|
func mustLoad(t *testing.T, fname string) *DB {
|
|
db, err := Load(fname)
|
|
if err != nil {
|
|
t.Fatalf("error loading database: %v", err)
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
func TestWrite(t *testing.T) {
|
|
fname := mustCreateDB(t, "")
|
|
defer removeIfSuccessful(t, fname)
|
|
db := mustLoad(t, fname)
|
|
|
|
if err := db.Write(); err != nil {
|
|
t.Fatalf("error writing database: %v", err)
|
|
}
|
|
|
|
// Load again, check it works and it's still empty.
|
|
db = mustLoad(t, fname)
|
|
if !dbEquals(emptyDB, db) {
|
|
t.Fatalf("expected %v, got %v", emptyDB, db)
|
|
}
|
|
|
|
// Add two users, write, and load again.
|
|
if err := db.AddUser("user1", "passwd1"); err != nil {
|
|
t.Fatalf("failed to add user1: %v", err)
|
|
}
|
|
if err := db.AddUser("ñoño", "añicos"); err != nil {
|
|
t.Fatalf("failed to add ñoño: %v", err)
|
|
}
|
|
if err := db.Write(); err != nil {
|
|
t.Fatalf("error writing database: %v", err)
|
|
}
|
|
|
|
db = mustLoad(t, fname)
|
|
for _, name := range []string{"user1", "ñoño"} {
|
|
if !db.HasUser(name) {
|
|
t.Errorf("user %q not in database", name)
|
|
}
|
|
if db.db.Users[name].GetScheme() == nil {
|
|
t.Errorf("user %q not using scrypt: %#v", name, db.db.Users[name])
|
|
}
|
|
}
|
|
|
|
// Check various user and password combinations, not all valid.
|
|
combinations := []struct {
|
|
user, passwd string
|
|
expected bool
|
|
}{
|
|
{"user1", "passwd1", true},
|
|
{"user1", "passwd", false},
|
|
{"user1", "passwd12", false},
|
|
{"ñoño", "añicos", true},
|
|
{"ñoño", "anicos", false},
|
|
{"notindb", "something", false},
|
|
{"", "", false},
|
|
{" ", " ", false},
|
|
}
|
|
for _, c := range combinations {
|
|
if db.Authenticate(c.user, c.passwd) != c.expected {
|
|
t.Errorf("auth(%q, %q) != %v", c.user, c.passwd, c.expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNew(t *testing.T) {
|
|
fname := fmt.Sprintf("%s/userdb_test-%d", os.TempDir(), os.Getpid())
|
|
defer os.Remove(fname)
|
|
db1 := New(fname)
|
|
db1.AddUser("user", "passwd")
|
|
db1.Write()
|
|
|
|
db2, err := Load(fname)
|
|
if err != nil {
|
|
t.Fatalf("error loading: %v", err)
|
|
}
|
|
|
|
if !dbEquals(db1, db2) {
|
|
t.Errorf("databases differ. db1:%v != db2:%v", db1, db2)
|
|
}
|
|
}
|
|
|
|
func TestInvalidUsername(t *testing.T) {
|
|
fname := mustCreateDB(t, "")
|
|
defer removeIfSuccessful(t, fname)
|
|
db := mustLoad(t, fname)
|
|
|
|
// Names that are invalid.
|
|
names := []string{
|
|
// Contain various types of spaces.
|
|
" ", " ", "a b", "ñ ñ", "a\xa0b", "a\x85b", "a\nb", "a\tb", "a\xffb",
|
|
|
|
// Contain characters not allowed by PRECIS.
|
|
"\u00b9", "\u2163",
|
|
|
|
// Names that are not normalized, but would otherwise be valid.
|
|
"A", "Ñ",
|
|
}
|
|
for _, name := range names {
|
|
err := db.AddUser(name, "passwd")
|
|
if err == nil {
|
|
t.Errorf("AddUser(%q) worked, expected it to fail", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func plainPassword(p string) *Password {
|
|
return &Password{
|
|
Scheme: &Password_Plain{&Plain{[]byte(p)}},
|
|
}
|
|
}
|
|
|
|
// Test the plain scheme. Note we don't expect to use it in cases other than
|
|
// debugging, but it should be functional for that purpose.
|
|
func TestPlainScheme(t *testing.T) {
|
|
fname := mustCreateDB(t, "")
|
|
defer removeIfSuccessful(t, fname)
|
|
db := mustLoad(t, fname)
|
|
|
|
db.db.Users["user"] = plainPassword("pass word")
|
|
err := db.Write()
|
|
if err != nil {
|
|
t.Errorf("Write failed: %v", err)
|
|
}
|
|
|
|
db = mustLoad(t, fname)
|
|
if !db.Authenticate("user", "pass word") {
|
|
t.Errorf("failed plain authentication")
|
|
}
|
|
if db.Authenticate("user", "wrong") {
|
|
t.Errorf("plain authentication worked but it shouldn't")
|
|
}
|
|
}
|
|
|
|
func TestReload(t *testing.T) {
|
|
content := "users:< key: 'u1' value:< plain:< password: 'pass' >>>"
|
|
fname := mustCreateDB(t, content)
|
|
defer removeIfSuccessful(t, fname)
|
|
db := mustLoad(t, fname)
|
|
|
|
// Add a valid line to the file.
|
|
content += "users:< key: 'u2' value:< plain:< password: 'pass' >>>"
|
|
ioutil.WriteFile(fname, []byte(content), 0660)
|
|
|
|
err := db.Reload()
|
|
if err != nil {
|
|
t.Errorf("Reload failed: %v", err)
|
|
}
|
|
if len(db.db.Users) != 2 {
|
|
t.Errorf("expected 2 users, got %d", len(db.db.Users))
|
|
}
|
|
|
|
// And now a broken one.
|
|
content += "users:< invalid >"
|
|
ioutil.WriteFile(fname, []byte(content), 0660)
|
|
|
|
err = db.Reload()
|
|
if err == nil {
|
|
t.Errorf("expected error, got nil")
|
|
}
|
|
if len(db.db.Users) != 2 {
|
|
t.Errorf("expected 2 users, got %d", len(db.db.Users))
|
|
}
|
|
|
|
// Cause an even bigger error loading, check the database is not changed.
|
|
db.fname = "/does/not/exist"
|
|
err = db.Reload()
|
|
if err == nil {
|
|
t.Errorf("expected error, got nil")
|
|
}
|
|
if len(db.db.Users) != 2 {
|
|
t.Errorf("expected 2 users, got %d", len(db.db.Users))
|
|
}
|
|
}
|
|
|
|
func TestRemoveUser(t *testing.T) {
|
|
fname := mustCreateDB(t, "")
|
|
defer removeIfSuccessful(t, fname)
|
|
db := mustLoad(t, fname)
|
|
|
|
if ok := db.RemoveUser("unknown"); ok {
|
|
t.Errorf("removal of unknown user succeeded")
|
|
}
|
|
|
|
if err := db.AddUser("user", "passwd"); err != nil {
|
|
t.Fatalf("error adding user: %v", err)
|
|
}
|
|
|
|
if ok := db.RemoveUser("unknown"); ok {
|
|
t.Errorf("removal of unknown user succeeded")
|
|
}
|
|
|
|
if ok := db.RemoveUser("user"); !ok {
|
|
t.Errorf("removal of existing user failed")
|
|
}
|
|
|
|
if ok := db.RemoveUser("user"); ok {
|
|
t.Errorf("removal of unknown user succeeded")
|
|
}
|
|
}
|
|
|
|
func TestHasUser(t *testing.T) {
|
|
fname := mustCreateDB(t, "")
|
|
defer removeIfSuccessful(t, fname)
|
|
db := mustLoad(t, fname)
|
|
|
|
if db.HasUser("unknown") {
|
|
t.Errorf("unknown user exists")
|
|
}
|
|
|
|
if err := db.AddUser("user", "passwd"); err != nil {
|
|
t.Fatalf("error adding user: %v", err)
|
|
}
|
|
|
|
if db.HasUser("unknown") {
|
|
t.Errorf("unknown user exists")
|
|
}
|
|
|
|
if !db.HasUser("user") {
|
|
t.Errorf("known user does not exist")
|
|
}
|
|
|
|
if !db.HasUser("user") {
|
|
t.Errorf("known user does not exist")
|
|
}
|
|
}
|