auth
2014-09-07
Child:3a6a65ed380c
auth/profile_test.go
Update profiles and add tests. Add tests for profile storage. Make Memstore implement the profilestore interface. Get the groundwork of profiles laid.
| paddy@38 | 1 package auth |
| paddy@38 | 2 |
| paddy@38 | 3 import ( |
| paddy@38 | 4 "fmt" |
| paddy@38 | 5 "testing" |
| paddy@38 | 6 "time" |
| paddy@38 | 7 |
| paddy@38 | 8 "secondbit.org/uuid" |
| paddy@38 | 9 ) |
| paddy@38 | 10 |
| paddy@38 | 11 const ( |
| paddy@38 | 12 profileChangeName = 1 << iota |
| paddy@38 | 13 profileChangePassphrase |
| paddy@38 | 14 profileChangeIterations |
| paddy@38 | 15 profileChangeSalt |
| paddy@38 | 16 profileChangePassphraseScheme |
| paddy@38 | 17 profileChangeCompromised |
| paddy@38 | 18 profileChangeLockedUntil |
| paddy@38 | 19 profileChangePassphraseReset |
| paddy@38 | 20 profileChangePassphraseResetCreated |
| paddy@38 | 21 profileChangeLastSeen |
| paddy@38 | 22 ) |
| paddy@38 | 23 |
| paddy@38 | 24 var profileStores = []ProfileStore{NewMemstore()} |
| paddy@38 | 25 |
| paddy@38 | 26 func compareProfiles(profile1, profile2 Profile) (success bool, field string, val1, val2 interface{}) { |
| paddy@38 | 27 if !profile1.ID.Equal(profile2.ID) { |
| paddy@38 | 28 return false, "ID", profile1.ID, profile2.ID |
| paddy@38 | 29 } |
| paddy@38 | 30 if profile1.Name != profile2.Name { |
| paddy@38 | 31 return false, "name", profile1.Name, profile2.Name |
| paddy@38 | 32 } |
| paddy@38 | 33 if profile1.Passphrase != profile2.Passphrase { |
| paddy@38 | 34 return false, "passphrase", profile1.Passphrase, profile2.Passphrase |
| paddy@38 | 35 } |
| paddy@38 | 36 if profile1.Iterations != profile2.Iterations { |
| paddy@38 | 37 return false, "iterations", profile1.Iterations, profile2.Iterations |
| paddy@38 | 38 } |
| paddy@38 | 39 if profile1.Salt != profile2.Salt { |
| paddy@38 | 40 return false, "salt", profile1.Salt, profile2.Salt |
| paddy@38 | 41 } |
| paddy@38 | 42 if profile1.PassphraseScheme != profile2.PassphraseScheme { |
| paddy@38 | 43 return false, "passphrase scheme", profile1.PassphraseScheme, profile2.PassphraseScheme |
| paddy@38 | 44 } |
| paddy@38 | 45 if profile1.Compromised != profile2.Compromised { |
| paddy@38 | 46 return false, "compromised", profile1.Compromised, profile2.Compromised |
| paddy@38 | 47 } |
| paddy@38 | 48 if !profile1.LockedUntil.Equal(profile2.LockedUntil) { |
| paddy@38 | 49 return false, "locked until", profile1.LockedUntil, profile2.LockedUntil |
| paddy@38 | 50 } |
| paddy@38 | 51 if profile1.PassphraseReset != profile2.PassphraseReset { |
| paddy@38 | 52 return false, "passphrase reset", profile1.PassphraseReset, profile2.PassphraseReset |
| paddy@38 | 53 } |
| paddy@38 | 54 if !profile1.PassphraseResetCreated.Equal(profile2.PassphraseResetCreated) { |
| paddy@38 | 55 return false, "passphrase reset created", profile1.PassphraseResetCreated, profile2.PassphraseResetCreated |
| paddy@38 | 56 } |
| paddy@38 | 57 if !profile1.Created.Equal(profile2.Created) { |
| paddy@38 | 58 return false, "created", profile1.Created, profile2.Created |
| paddy@38 | 59 } |
| paddy@38 | 60 if !profile1.LastSeen.Equal(profile2.LastSeen) { |
| paddy@38 | 61 return false, "last seen", profile1.LastSeen, profile2.LastSeen |
| paddy@38 | 62 } |
| paddy@38 | 63 return true, "", nil, nil |
| paddy@38 | 64 } |
| paddy@38 | 65 |
| paddy@38 | 66 func TestProfileStoreSuccess(t *testing.T) { |
| paddy@38 | 67 t.Parallel() |
| paddy@38 | 68 profile := Profile{ |
| paddy@38 | 69 ID: uuid.NewID(), |
| paddy@38 | 70 Name: "name", |
| paddy@38 | 71 Passphrase: "passphrase", |
| paddy@38 | 72 Iterations: 10000, |
| paddy@38 | 73 Salt: "salt", |
| paddy@38 | 74 PassphraseScheme: 1, |
| paddy@38 | 75 Compromised: false, |
| paddy@38 | 76 LockedUntil: time.Now().Add(time.Hour), |
| paddy@38 | 77 PassphraseReset: "passphrase reset", |
| paddy@38 | 78 PassphraseResetCreated: time.Now(), |
| paddy@38 | 79 Created: time.Now(), |
| paddy@38 | 80 LastSeen: time.Now(), |
| paddy@38 | 81 } |
| paddy@38 | 82 for _, store := range profileStores { |
| paddy@38 | 83 err := store.SaveProfile(profile) |
| paddy@38 | 84 if err != nil { |
| paddy@38 | 85 t.Errorf("Error saving profile to %T: %s", store, err) |
| paddy@38 | 86 } |
| paddy@38 | 87 err = store.SaveProfile(profile) |
| paddy@38 | 88 if err != ErrProfileAlreadyExists { |
| paddy@38 | 89 t.Errorf("Expected ErrProfileAlreadyExists from %T, got %+v", store, err) |
| paddy@38 | 90 } |
| paddy@38 | 91 retrieved, err := store.GetProfileByID(profile.ID) |
| paddy@38 | 92 if err != nil { |
| paddy@38 | 93 t.Errorf("Error retrieving profile from %T: %s", store, err) |
| paddy@38 | 94 } |
| paddy@38 | 95 match, field, expectation, result := compareProfiles(profile, retrieved) |
| paddy@38 | 96 if !match { |
| paddy@38 | 97 t.Errorf("Expected `%v` in the `%s` field of profile retrieved from %T, got `%v`", expectation, field, store, result) |
| paddy@38 | 98 } |
| paddy@38 | 99 err = store.DeleteProfile(profile.ID) |
| paddy@38 | 100 if err != nil { |
| paddy@38 | 101 t.Errorf("Error removing profile from %T: %s", store, err) |
| paddy@38 | 102 } |
| paddy@38 | 103 retrieved, err = store.GetProfileByID(profile.ID) |
| paddy@38 | 104 if err != ErrProfileNotFound { |
| paddy@38 | 105 t.Errorf("Expected ErrProfileNotFound from %T, got %+v and %+v", store, retrieved, err) |
| paddy@38 | 106 } |
| paddy@38 | 107 err = store.DeleteProfile(profile.ID) |
| paddy@38 | 108 if err != ErrProfileNotFound { |
| paddy@38 | 109 t.Errorf("Expected ErrProfileNotFound from %T, got %+v", store, err) |
| paddy@38 | 110 } |
| paddy@38 | 111 } |
| paddy@38 | 112 } |
| paddy@38 | 113 |
| paddy@38 | 114 func TestProfileUpdates(t *testing.T) { |
| paddy@38 | 115 t.Parallel() |
| paddy@38 | 116 variations := 1 << 10 |
| paddy@38 | 117 profile := Profile{ |
| paddy@38 | 118 ID: uuid.NewID(), |
| paddy@38 | 119 Name: "name", |
| paddy@38 | 120 Passphrase: "passphrase", |
| paddy@38 | 121 Iterations: 10000, |
| paddy@38 | 122 Salt: "salt", |
| paddy@38 | 123 PassphraseScheme: 1, |
| paddy@38 | 124 Compromised: false, |
| paddy@38 | 125 LockedUntil: time.Now().Add(time.Hour), |
| paddy@38 | 126 PassphraseReset: "passphrase reset", |
| paddy@38 | 127 PassphraseResetCreated: time.Now(), |
| paddy@38 | 128 Created: time.Now(), |
| paddy@38 | 129 LastSeen: time.Now(), |
| paddy@38 | 130 } |
| paddy@38 | 131 for i := 0; i < variations; i++ { |
| paddy@38 | 132 var name, passphrase, salt, passphraseReset string |
| paddy@38 | 133 var iterations int64 |
| paddy@38 | 134 var lockedUntil, passphraseResetCreated, lastSeen time.Time |
| paddy@38 | 135 var passphraseScheme int |
| paddy@38 | 136 var compromised bool |
| paddy@38 | 137 |
| paddy@38 | 138 change := ProfileChange{} |
| paddy@38 | 139 expectation := profile |
| paddy@38 | 140 result := profile |
| paddy@38 | 141 if i&profileChangeName != 0 { |
| paddy@38 | 142 name = fmt.Sprintf("name-%d", i) |
| paddy@38 | 143 change.Name = &name |
| paddy@38 | 144 expectation.Name = name |
| paddy@38 | 145 } |
| paddy@38 | 146 if i&profileChangePassphrase != 0 { |
| paddy@38 | 147 passphrase = fmt.Sprintf("passphrase-%d", i) |
| paddy@38 | 148 change.Passphrase = &passphrase |
| paddy@38 | 149 expectation.Passphrase = passphrase |
| paddy@38 | 150 } |
| paddy@38 | 151 if i&profileChangeIterations != 0 { |
| paddy@38 | 152 iterations = int64(i) |
| paddy@38 | 153 change.Iterations = &iterations |
| paddy@38 | 154 expectation.Iterations = iterations |
| paddy@38 | 155 } |
| paddy@38 | 156 if i&profileChangeSalt != 0 { |
| paddy@38 | 157 salt = fmt.Sprintf("salt-%d", i) |
| paddy@38 | 158 change.Salt = &salt |
| paddy@38 | 159 expectation.Salt = salt |
| paddy@38 | 160 } |
| paddy@38 | 161 if i&profileChangePassphraseScheme != 0 { |
| paddy@38 | 162 passphraseScheme = i |
| paddy@38 | 163 change.PassphraseScheme = &passphraseScheme |
| paddy@38 | 164 expectation.PassphraseScheme = passphraseScheme |
| paddy@38 | 165 } |
| paddy@38 | 166 if i&profileChangeCompromised != 0 { |
| paddy@38 | 167 compromised = i%2 != 0 |
| paddy@38 | 168 change.Compromised = &compromised |
| paddy@38 | 169 expectation.Compromised = compromised |
| paddy@38 | 170 } |
| paddy@38 | 171 if i&profileChangeLockedUntil != 0 { |
| paddy@38 | 172 lockedUntil = time.Now() |
| paddy@38 | 173 change.LockedUntil = &lockedUntil |
| paddy@38 | 174 expectation.LockedUntil = lockedUntil |
| paddy@38 | 175 } |
| paddy@38 | 176 if i&profileChangePassphraseReset != 0 { |
| paddy@38 | 177 passphraseReset = fmt.Sprintf("passphraseReset-%d", i) |
| paddy@38 | 178 change.PassphraseReset = &passphraseReset |
| paddy@38 | 179 expectation.PassphraseReset = passphraseReset |
| paddy@38 | 180 } |
| paddy@38 | 181 if i&profileChangePassphraseResetCreated != 0 { |
| paddy@38 | 182 passphraseResetCreated = time.Now() |
| paddy@38 | 183 change.PassphraseResetCreated = &passphraseResetCreated |
| paddy@38 | 184 expectation.PassphraseResetCreated = passphraseResetCreated |
| paddy@38 | 185 } |
| paddy@38 | 186 if i&profileChangeLastSeen != 0 { |
| paddy@38 | 187 lastSeen = time.Now() |
| paddy@38 | 188 change.LastSeen = &lastSeen |
| paddy@38 | 189 expectation.LastSeen = lastSeen |
| paddy@38 | 190 } |
| paddy@38 | 191 result.ApplyChange(change) |
| paddy@38 | 192 match, field, expected, got := compareProfiles(expectation, result) |
| paddy@38 | 193 if !match { |
| paddy@38 | 194 t.Errorf("Expected field `%s` to be `%v`, got `%v`", field, expected, got) |
| paddy@38 | 195 } |
| paddy@38 | 196 for _, store := range profileStores { |
| paddy@38 | 197 err := store.SaveProfile(profile) |
| paddy@38 | 198 if err != nil { |
| paddy@38 | 199 t.Errorf("Error saving profile in %T: %s", store, err) |
| paddy@38 | 200 } |
| paddy@38 | 201 err = store.UpdateProfile(profile.ID, change) |
| paddy@38 | 202 if err != nil { |
| paddy@38 | 203 t.Errorf("Error updating profile in %T: %s", store, err) |
| paddy@38 | 204 } |
| paddy@38 | 205 retrieved, err := store.GetProfileByID(profile.ID) |
| paddy@38 | 206 if err != nil { |
| paddy@38 | 207 t.Errorf("Error getting profile from %T: %s", store, err) |
| paddy@38 | 208 } |
| paddy@38 | 209 match, field, expected, got = compareProfiles(expectation, retrieved) |
| paddy@38 | 210 if !match { |
| paddy@38 | 211 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store) |
| paddy@38 | 212 } |
| paddy@38 | 213 err = store.DeleteProfile(profile.ID) |
| paddy@38 | 214 if err != nil { |
| paddy@38 | 215 t.Errorf("Error deleting profile from %T: %s", store, err) |
| paddy@38 | 216 } |
| paddy@38 | 217 err = store.UpdateProfile(profile.ID, change) |
| paddy@38 | 218 if err != ErrProfileNotFound { |
| paddy@38 | 219 t.Errorf("Expected ErrProfileNotFound, got %v from %T", err, store) |
| paddy@38 | 220 } |
| paddy@38 | 221 } |
| paddy@38 | 222 } |
| paddy@38 | 223 } |