auth

Paddy 2015-03-21 Parent:06fb735031bb Child:3e8964a914ef

149:8267e1c8bcd1 Go to Latest

auth/profile_test.go

Test our Postgres profileStore implementation. Update all our test cases to use time.Now().Round(time.Millisecond), because Go uses nanosecond precision on time values, but Postgres silently truncates that to millisecond precision. This caused our tests to report false failures that were just silent precision loss, not actual failures. Set up our authd server to use the Postgres store for profiles and automatically create a test scope when starting up. Log errors when creating Clients through the API, instead of just swallowing them and sending back cryptic act of god errors. Add a NewPostgres helper that returns a postgres profileStore from a connection string (passed through pq transparently). Add an Empty() bool helper to ProfileChange and BulkProfileChange types, so we can determine if there are any changes we need to act on easily. Log errors when creating Pofiles through the API, instead of just swalloing them and sending back cryptic act of god errors. Remove the ` quotes around field and table names, which are not supported in Postgres. This required adding a few functions/methods to pan. Detect situations where a profile was expected and not found, and return ErrProfileNotFound. Detect pq errors thrown when the profiles_pkey constraint is violated, and transform them to the ErrProfileAlreadyExists error. Detect empty ProfileChange and BulkProfileChange variables and abort the updateProfile and updateProfiles methods early, before invalid SQL is generated. Detect pq errors thrown when the logins_pkey constraint is violated, and transform them to the ErrLoginAlreadyExists error. Detect when removing a Login and no rows were affected, and return an ErrLoginNotFound. Create an sql dir with a postgres_init script that will initialize the schema of the tables expected in the database.

History
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@107 8 "code.secondbit.org/uuid.hg"
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@149 24 func init() {
paddy@149 25 p, err := NewPostgres("dbname=testdb sslmode=disable")
paddy@149 26 if err != nil {
paddy@149 27 panic(err)
paddy@149 28 }
paddy@149 29 if !testing.Short() {
paddy@149 30 profileStores = append(profileStores, &p)
paddy@149 31 }
paddy@149 32 }
paddy@149 33
paddy@57 34 var profileStores = []profileStore{NewMemstore()}
paddy@38 35
paddy@38 36 func compareProfiles(profile1, profile2 Profile) (success bool, field string, val1, val2 interface{}) {
paddy@38 37 if !profile1.ID.Equal(profile2.ID) {
paddy@38 38 return false, "ID", profile1.ID, profile2.ID
paddy@38 39 }
paddy@38 40 if profile1.Name != profile2.Name {
paddy@38 41 return false, "name", profile1.Name, profile2.Name
paddy@38 42 }
paddy@38 43 if profile1.Passphrase != profile2.Passphrase {
paddy@38 44 return false, "passphrase", profile1.Passphrase, profile2.Passphrase
paddy@38 45 }
paddy@38 46 if profile1.Iterations != profile2.Iterations {
paddy@38 47 return false, "iterations", profile1.Iterations, profile2.Iterations
paddy@38 48 }
paddy@38 49 if profile1.Salt != profile2.Salt {
paddy@38 50 return false, "salt", profile1.Salt, profile2.Salt
paddy@38 51 }
paddy@38 52 if profile1.PassphraseScheme != profile2.PassphraseScheme {
paddy@38 53 return false, "passphrase scheme", profile1.PassphraseScheme, profile2.PassphraseScheme
paddy@38 54 }
paddy@38 55 if profile1.Compromised != profile2.Compromised {
paddy@38 56 return false, "compromised", profile1.Compromised, profile2.Compromised
paddy@38 57 }
paddy@38 58 if !profile1.LockedUntil.Equal(profile2.LockedUntil) {
paddy@38 59 return false, "locked until", profile1.LockedUntil, profile2.LockedUntil
paddy@38 60 }
paddy@38 61 if profile1.PassphraseReset != profile2.PassphraseReset {
paddy@38 62 return false, "passphrase reset", profile1.PassphraseReset, profile2.PassphraseReset
paddy@38 63 }
paddy@38 64 if !profile1.PassphraseResetCreated.Equal(profile2.PassphraseResetCreated) {
paddy@38 65 return false, "passphrase reset created", profile1.PassphraseResetCreated, profile2.PassphraseResetCreated
paddy@38 66 }
paddy@38 67 if !profile1.Created.Equal(profile2.Created) {
paddy@38 68 return false, "created", profile1.Created, profile2.Created
paddy@38 69 }
paddy@38 70 if !profile1.LastSeen.Equal(profile2.LastSeen) {
paddy@38 71 return false, "last seen", profile1.LastSeen, profile2.LastSeen
paddy@38 72 }
paddy@148 73 if profile1.Deleted != profile2.Deleted {
paddy@148 74 return false, "deleted", profile1.Deleted, profile2.Deleted
paddy@148 75 }
paddy@38 76 return true, "", nil, nil
paddy@38 77 }
paddy@38 78
paddy@46 79 func compareLogins(login1, login2 Login) (success bool, field string, val1, val2 interface{}) {
paddy@46 80 if login1.Type != login2.Type {
paddy@46 81 return false, "Type", login1.Type, login2.Type
paddy@46 82 }
paddy@46 83 if login1.Value != login2.Value {
paddy@46 84 return false, "Value", login1.Value, login2.Value
paddy@46 85 }
paddy@46 86 if !login1.ProfileID.Equal(login2.ProfileID) {
paddy@46 87 return false, "ProfileID", login1.ProfileID, login2.ProfileID
paddy@46 88 }
paddy@46 89 if !login1.Created.Equal(login2.Created) {
paddy@46 90 return false, "Created", login1.Created, login2.Created
paddy@46 91 }
paddy@46 92 if !login1.LastUsed.Equal(login2.LastUsed) {
paddy@46 93 return false, "LastUsed", login1.LastUsed, login2.LastUsed
paddy@46 94 }
paddy@46 95 return true, "", nil, nil
paddy@46 96 }
paddy@46 97
paddy@38 98 func TestProfileStoreSuccess(t *testing.T) {
paddy@38 99 t.Parallel()
paddy@38 100 profile := Profile{
paddy@38 101 ID: uuid.NewID(),
paddy@38 102 Name: "name",
paddy@38 103 Passphrase: "passphrase",
paddy@38 104 Iterations: 10000,
paddy@38 105 Salt: "salt",
paddy@38 106 PassphraseScheme: 1,
paddy@38 107 Compromised: false,
paddy@149 108 LockedUntil: time.Now().Add(time.Hour).Round(time.Millisecond),
paddy@38 109 PassphraseReset: "passphrase reset",
paddy@149 110 PassphraseResetCreated: time.Now().Round(time.Millisecond),
paddy@149 111 Created: time.Now().Round(time.Millisecond),
paddy@149 112 LastSeen: time.Now().Round(time.Millisecond),
paddy@38 113 }
paddy@38 114 for _, store := range profileStores {
paddy@116 115 context := Context{profiles: store}
paddy@116 116 err := context.SaveProfile(profile)
paddy@38 117 if err != nil {
paddy@38 118 t.Errorf("Error saving profile to %T: %s", store, err)
paddy@38 119 }
paddy@116 120 err = context.SaveProfile(profile)
paddy@38 121 if err != ErrProfileAlreadyExists {
paddy@38 122 t.Errorf("Expected ErrProfileAlreadyExists from %T, got %+v", store, err)
paddy@38 123 }
paddy@116 124 retrieved, err := context.GetProfileByID(profile.ID)
paddy@38 125 if err != nil {
paddy@38 126 t.Errorf("Error retrieving profile from %T: %s", store, err)
paddy@38 127 }
paddy@38 128 match, field, expectation, result := compareProfiles(profile, retrieved)
paddy@38 129 if !match {
paddy@38 130 t.Errorf("Expected `%v` in the `%s` field of profile retrieved from %T, got `%v`", expectation, field, store, result)
paddy@38 131 }
paddy@148 132 deleted := true
paddy@148 133 err = context.UpdateProfile(profile.ID, ProfileChange{Deleted: &deleted})
paddy@38 134 if err != nil {
paddy@38 135 t.Errorf("Error removing profile from %T: %s", store, err)
paddy@38 136 }
paddy@116 137 retrieved, err = context.GetProfileByID(profile.ID)
paddy@38 138 if err != ErrProfileNotFound {
paddy@38 139 t.Errorf("Expected ErrProfileNotFound from %T, got %+v and %+v", store, retrieved, err)
paddy@38 140 }
paddy@38 141 }
paddy@38 142 }
paddy@38 143
paddy@38 144 func TestProfileUpdates(t *testing.T) {
paddy@38 145 t.Parallel()
paddy@38 146 variations := 1 << 10
paddy@38 147 profile := Profile{
paddy@38 148 ID: uuid.NewID(),
paddy@38 149 Name: "name",
paddy@38 150 Passphrase: "passphrase",
paddy@38 151 Iterations: 10000,
paddy@38 152 Salt: "salt",
paddy@38 153 PassphraseScheme: 1,
paddy@38 154 Compromised: false,
paddy@149 155 LockedUntil: time.Now().Add(time.Hour).Round(time.Millisecond),
paddy@38 156 PassphraseReset: "passphrase reset",
paddy@149 157 PassphraseResetCreated: time.Now().Round(time.Millisecond),
paddy@149 158 Created: time.Now().Round(time.Millisecond),
paddy@149 159 LastSeen: time.Now().Round(time.Millisecond),
paddy@38 160 }
paddy@38 161 for i := 0; i < variations; i++ {
paddy@38 162 var name, passphrase, salt, passphraseReset string
paddy@69 163 var iterations int
paddy@38 164 var lockedUntil, passphraseResetCreated, lastSeen time.Time
paddy@38 165 var passphraseScheme int
paddy@38 166 var compromised bool
paddy@38 167
paddy@148 168 profile.ID = uuid.NewID()
paddy@38 169 change := ProfileChange{}
paddy@38 170 expectation := profile
paddy@38 171 result := profile
paddy@38 172 if i&profileChangeName != 0 {
paddy@38 173 name = fmt.Sprintf("name-%d", i)
paddy@38 174 change.Name = &name
paddy@38 175 expectation.Name = name
paddy@38 176 }
paddy@38 177 if i&profileChangePassphrase != 0 {
paddy@38 178 passphrase = fmt.Sprintf("passphrase-%d", i)
paddy@38 179 change.Passphrase = &passphrase
paddy@38 180 expectation.Passphrase = passphrase
paddy@38 181 }
paddy@38 182 if i&profileChangeIterations != 0 {
paddy@69 183 iterations = i
paddy@38 184 change.Iterations = &iterations
paddy@38 185 expectation.Iterations = iterations
paddy@38 186 }
paddy@38 187 if i&profileChangeSalt != 0 {
paddy@38 188 salt = fmt.Sprintf("salt-%d", i)
paddy@38 189 change.Salt = &salt
paddy@38 190 expectation.Salt = salt
paddy@38 191 }
paddy@38 192 if i&profileChangePassphraseScheme != 0 {
paddy@38 193 passphraseScheme = i
paddy@38 194 change.PassphraseScheme = &passphraseScheme
paddy@38 195 expectation.PassphraseScheme = passphraseScheme
paddy@38 196 }
paddy@38 197 if i&profileChangeCompromised != 0 {
paddy@38 198 compromised = i%2 != 0
paddy@38 199 change.Compromised = &compromised
paddy@38 200 expectation.Compromised = compromised
paddy@38 201 }
paddy@38 202 if i&profileChangeLockedUntil != 0 {
paddy@149 203 lockedUntil = time.Now().Round(time.Millisecond)
paddy@38 204 change.LockedUntil = &lockedUntil
paddy@38 205 expectation.LockedUntil = lockedUntil
paddy@38 206 }
paddy@38 207 if i&profileChangePassphraseReset != 0 {
paddy@38 208 passphraseReset = fmt.Sprintf("passphraseReset-%d", i)
paddy@38 209 change.PassphraseReset = &passphraseReset
paddy@38 210 expectation.PassphraseReset = passphraseReset
paddy@38 211 }
paddy@38 212 if i&profileChangePassphraseResetCreated != 0 {
paddy@149 213 passphraseResetCreated = time.Now().Round(time.Millisecond)
paddy@38 214 change.PassphraseResetCreated = &passphraseResetCreated
paddy@38 215 expectation.PassphraseResetCreated = passphraseResetCreated
paddy@38 216 }
paddy@38 217 if i&profileChangeLastSeen != 0 {
paddy@149 218 lastSeen = time.Now().Round(time.Millisecond)
paddy@38 219 change.LastSeen = &lastSeen
paddy@38 220 expectation.LastSeen = lastSeen
paddy@38 221 }
paddy@38 222 result.ApplyChange(change)
paddy@38 223 match, field, expected, got := compareProfiles(expectation, result)
paddy@38 224 if !match {
paddy@38 225 t.Errorf("Expected field `%s` to be `%v`, got `%v`", field, expected, got)
paddy@38 226 }
paddy@38 227 for _, store := range profileStores {
paddy@116 228 context := Context{profiles: store}
paddy@116 229 err := context.SaveProfile(profile)
paddy@38 230 if err != nil {
paddy@38 231 t.Errorf("Error saving profile in %T: %s", store, err)
paddy@38 232 }
paddy@116 233 err = context.UpdateProfile(profile.ID, change)
paddy@38 234 if err != nil {
paddy@38 235 t.Errorf("Error updating profile in %T: %s", store, err)
paddy@38 236 }
paddy@116 237 retrieved, err := context.GetProfileByID(profile.ID)
paddy@38 238 if err != nil {
paddy@38 239 t.Errorf("Error getting profile from %T: %s", store, err)
paddy@38 240 }
paddy@38 241 match, field, expected, got = compareProfiles(expectation, retrieved)
paddy@38 242 if !match {
paddy@38 243 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store)
paddy@38 244 }
paddy@38 245 }
paddy@38 246 }
paddy@38 247 }
paddy@45 248
paddy@45 249 func TestProfilesUpdates(t *testing.T) {
paddy@45 250 profile1 := Profile{
paddy@45 251 ID: uuid.NewID(),
paddy@45 252 }
paddy@45 253 profile2 := Profile{
paddy@45 254 ID: uuid.NewID(),
paddy@45 255 }
paddy@45 256 profile3 := Profile{
paddy@45 257 ID: uuid.NewID(),
paddy@45 258 }
paddy@45 259 truth := true
paddy@45 260 change := BulkProfileChange{
paddy@45 261 Compromised: &truth,
paddy@45 262 }
paddy@45 263 for _, store := range profileStores {
paddy@116 264 context := Context{profiles: store}
paddy@116 265 err := context.SaveProfile(profile1)
paddy@45 266 if err != nil {
paddy@45 267 t.Errorf("Error saving profile in %T: %s", store, err)
paddy@45 268 }
paddy@116 269 err = context.SaveProfile(profile2)
paddy@45 270 if err != nil {
paddy@45 271 t.Errorf("Error saving profile in %T: %s", store, err)
paddy@45 272 }
paddy@116 273 err = context.SaveProfile(profile3)
paddy@45 274 if err != nil {
paddy@45 275 t.Errorf("Error saving profile in %T: %s", store, err)
paddy@45 276 }
paddy@116 277 err = context.UpdateProfiles([]uuid.ID{profile1.ID, profile3.ID}, change)
paddy@45 278 if err != nil {
paddy@45 279 t.Errorf("Error updating profile in %T: %s", store, err)
paddy@45 280 }
paddy@45 281 profile1.Compromised = truth
paddy@45 282 profile3.Compromised = truth
paddy@116 283 retrieved, err := context.GetProfileByID(profile1.ID)
paddy@45 284 if err != nil {
paddy@45 285 t.Errorf("Error getting profile from %T: %s", store, err)
paddy@45 286 }
paddy@45 287 match, field, expected, got := compareProfiles(profile1, retrieved)
paddy@45 288 if !match {
paddy@45 289 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store)
paddy@45 290 }
paddy@116 291 retrieved, err = context.GetProfileByID(profile2.ID)
paddy@45 292 if err != nil {
paddy@45 293 t.Errorf("Error getting profile from %T: %s", store, err)
paddy@45 294 }
paddy@45 295 match, field, expected, got = compareProfiles(profile2, retrieved)
paddy@45 296 if !match {
paddy@45 297 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store)
paddy@45 298 }
paddy@116 299 retrieved, err = context.GetProfileByID(profile3.ID)
paddy@45 300 if err != nil {
paddy@45 301 t.Errorf("Error getting profile from %T: %s", store, err)
paddy@45 302 }
paddy@45 303 match, field, expected, got = compareProfiles(profile3, retrieved)
paddy@45 304 if !match {
paddy@45 305 t.Errorf("Expected field `%s` to be `%v`, got `%v` from %T", field, expected, got, store)
paddy@45 306 }
paddy@45 307 }
paddy@45 308 }
paddy@46 309
paddy@46 310 func TestProfileStoreLoginSuccess(t *testing.T) {
paddy@46 311 t.Parallel()
paddy@46 312 login := Login{
paddy@46 313 Type: "type",
paddy@46 314 Value: "value",
paddy@46 315 ProfileID: uuid.NewID(),
paddy@149 316 Created: time.Now().Add(-1 * time.Hour).Round(time.Millisecond),
paddy@149 317 LastUsed: time.Now().Add(-1 * time.Minute).Round(time.Millisecond),
paddy@46 318 }
paddy@46 319 for _, store := range profileStores {
paddy@116 320 context := Context{profiles: store}
paddy@116 321 err := context.AddLogin(login)
paddy@46 322 if err != nil {
paddy@46 323 t.Errorf("Error adding login to %T: %s", store, err)
paddy@46 324 }
paddy@116 325 err = context.AddLogin(login)
paddy@46 326 if err != ErrLoginAlreadyExists {
paddy@46 327 t.Errorf("Expected ErrLoginAlreadyExists from %T, got %+v", store, err)
paddy@46 328 }
paddy@116 329 retrieved, err := context.ListLogins(login.ProfileID, 10, 0)
paddy@46 330 if err != nil {
paddy@46 331 t.Errorf("Error retrieving logins from %T: %s", store, err)
paddy@46 332 }
paddy@46 333 if len(retrieved) != 1 {
paddy@46 334 t.Errorf("Expected 1 login result from %T, got %d", store, len(retrieved))
paddy@46 335 }
paddy@46 336 match, field, expectation, result := compareLogins(login, retrieved[0])
paddy@46 337 if !match {
paddy@46 338 t.Errorf("Expected `%v` in the `%s` field of login retrieved from %T, got `%v`", expectation, field, store, result)
paddy@46 339 }
paddy@149 340 lastUsed := time.Now().Round(time.Millisecond)
paddy@116 341 err = context.RecordLoginUse(login.Value, lastUsed)
paddy@46 342 if err != nil {
paddy@46 343 t.Errorf("Error recording use of login to %T: %s", store, err)
paddy@46 344 }
paddy@46 345 login.LastUsed = lastUsed
paddy@116 346 retrieved, err = context.ListLogins(login.ProfileID, 10, 0)
paddy@46 347 if err != nil {
paddy@46 348 t.Errorf("Error retrieving logins from %T: %s", store, err)
paddy@46 349 }
paddy@46 350 if len(retrieved) != 1 {
paddy@46 351 t.Errorf("Expected 1 login result from %T, got %d", store, len(retrieved))
paddy@46 352 }
paddy@46 353 match, field, expectation, result = compareLogins(login, retrieved[0])
paddy@46 354 if !match {
paddy@46 355 t.Errorf("Expected `%v` in the `%s` field of login retrieved from %T, got `%v`", expectation, field, store, result)
paddy@46 356 }
paddy@116 357 err = context.RemoveLogin(login.Value, login.ProfileID)
paddy@46 358 if err != nil {
paddy@46 359 t.Errorf("Error removing login from %T: %s", store, err)
paddy@46 360 }
paddy@116 361 retrieved, err = context.ListLogins(login.ProfileID, 10, 0)
paddy@46 362 if len(retrieved) != 0 {
paddy@46 363 t.Errorf("Expected 0 login results from %T, got %d: %+v", store, len(retrieved), retrieved)
paddy@46 364 }
paddy@116 365 err = context.RemoveLogin(login.Value, login.ProfileID)
paddy@46 366 if err != ErrLoginNotFound {
paddy@46 367 t.Errorf("Expected ErrLoginNotFound from %T, got %+v", store, err)
paddy@46 368 }
paddy@46 369 }
paddy@46 370 }
paddy@47 371
paddy@47 372 func TestProfileStoreLoginRetrieval(t *testing.T) {
paddy@47 373 t.Parallel()
paddy@47 374 profile := Profile{
paddy@47 375 ID: uuid.NewID(),
paddy@47 376 Name: "name",
paddy@47 377 Passphrase: "passphrase",
paddy@47 378 Iterations: 10000,
paddy@47 379 Salt: "salt",
paddy@47 380 PassphraseScheme: 1,
paddy@47 381 Compromised: false,
paddy@149 382 LockedUntil: time.Now().Add(time.Hour).Round(time.Millisecond),
paddy@47 383 PassphraseReset: "passphrase reset",
paddy@149 384 PassphraseResetCreated: time.Now().Round(time.Millisecond),
paddy@149 385 Created: time.Now().Round(time.Millisecond),
paddy@149 386 LastSeen: time.Now().Round(time.Millisecond),
paddy@47 387 }
paddy@47 388 login := Login{
paddy@47 389 Type: "type",
paddy@47 390 Value: "value",
paddy@47 391 ProfileID: profile.ID,
paddy@149 392 Created: time.Now().Add(-1 * time.Hour).Round(time.Millisecond),
paddy@149 393 LastUsed: time.Now().Add(-1 * time.Minute).Round(time.Millisecond),
paddy@47 394 }
paddy@47 395 for _, store := range profileStores {
paddy@116 396 context := Context{profiles: store}
paddy@116 397 err := context.SaveProfile(profile)
paddy@47 398 if err != nil {
paddy@47 399 t.Errorf("Error saving profile in %T: %s", store, err)
paddy@47 400 }
paddy@116 401 err = context.AddLogin(login)
paddy@47 402 if err != nil {
paddy@47 403 t.Errorf("Error storing login in %T: %s", store, err)
paddy@47 404 }
paddy@116 405 retrieved, err := context.GetProfileByLogin(login.Value)
paddy@47 406 if err != nil {
paddy@47 407 t.Errorf("Error retrieving profile by login from %T: %s", store, err)
paddy@47 408 }
paddy@47 409 match, field, expectation, result := compareProfiles(profile, retrieved)
paddy@47 410 if !match {
paddy@47 411 t.Errorf("Expected `%v` in the `%s` field of profile retrieved from %T, got `%v`", expectation, field, store, result)
paddy@47 412 }
paddy@47 413 }
paddy@47 414 }
paddy@48 415
paddy@48 416 func TestProfileChangeValidation(t *testing.T) {
paddy@48 417 t.Parallel()
paddy@48 418 passphraseScheme := 1
paddy@48 419 passphraseReset := "reset"
paddy@48 420 salt := "salt"
paddy@69 421 iterations := 100
paddy@48 422 emptyName := ""
paddy@48 423 enteredName := "Paddy"
paddy@48 424 okPassphrase := "this is a decent passphrase"
paddy@48 425 compromised := true
paddy@149 426 lockedUntil := time.Now().Round(time.Millisecond)
paddy@149 427 resetCreated := time.Now().Round(time.Millisecond)
paddy@149 428 lastSeen := time.Now().Round(time.Millisecond)
paddy@48 429 changes := map[*ProfileChange]error{
paddy@48 430 &ProfileChange{}: ErrEmptyChange,
paddy@48 431 &ProfileChange{PassphraseScheme: &passphraseScheme}: ErrMissingPassphrase,
paddy@48 432 &ProfileChange{PassphraseScheme: &passphraseScheme, Passphrase: &okPassphrase}: nil,
paddy@48 433 &ProfileChange{PassphraseReset: &passphraseReset}: ErrMissingPassphraseResetCreated,
paddy@48 434 &ProfileChange{PassphraseReset: &passphraseReset, PassphraseResetCreated: &resetCreated}: nil,
paddy@48 435 &ProfileChange{Salt: &salt}: ErrMissingPassphrase,
paddy@48 436 &ProfileChange{Salt: &salt, Passphrase: &okPassphrase}: nil,
paddy@48 437 &ProfileChange{Iterations: &iterations}: ErrMissingPassphrase,
paddy@48 438 &ProfileChange{Iterations: &iterations, Passphrase: &okPassphrase}: nil,
paddy@48 439 &ProfileChange{Passphrase: &okPassphrase}: nil,
paddy@48 440 &ProfileChange{Name: &emptyName}: nil,
paddy@48 441 &ProfileChange{Name: &enteredName}: nil,
paddy@48 442 &ProfileChange{Compromised: &compromised}: nil,
paddy@48 443 &ProfileChange{LockedUntil: &lockedUntil}: nil,
paddy@48 444 &ProfileChange{LastSeen: &lastSeen}: nil,
paddy@48 445 &ProfileChange{PassphraseResetCreated: &resetCreated}: ErrMissingPassphraseReset,
paddy@48 446 }
paddy@48 447 for change, expectedErr := range changes {
paddy@48 448 if err := change.Validate(); err != expectedErr {
paddy@48 449 t.Errorf("Expected %+v to give an error of %v, gave %v", change, expectedErr, err)
paddy@48 450 }
paddy@48 451 }
paddy@48 452 }
paddy@48 453
paddy@48 454 func TestBulkProfileChangeValidation(t *testing.T) {
paddy@48 455 t.Parallel()
paddy@48 456 compromised := true
paddy@48 457 changes := map[*BulkProfileChange]error{
paddy@48 458 &BulkProfileChange{}: ErrEmptyChange,
paddy@48 459 &BulkProfileChange{Compromised: &compromised}: nil,
paddy@48 460 }
paddy@48 461 for change, expectedErr := range changes {
paddy@48 462 if err := change.Validate(); err != expectedErr {
paddy@48 463 t.Errorf("Expected %+v to give an error of %v, gave %v", change, expectedErr, err)
paddy@48 464 }
paddy@48 465 }
paddy@48 466 }
paddy@128 467
paddy@128 468 // BUG(paddy): We need to test the validateNewProfileRequest helper.
paddy@128 469 // BUG(paddy): We need to test the CreateProfileHandler.
paddy@148 470 // BUG(paddy): We need to test that deleting works as we expect.