auth

Paddy 2015-05-17 Parent:849f3820b164 Child:8ecb60d29b0d

169:37a42585660e Go to Latest

auth/profile_test.go

Create interfaces for login verification flow. We needed an interface that we could use to say "send the email to verify the user's login" so that we could verify the emails we have are actually valid. This implements an NSQ version that sends an email_verification event. We'll get listener implementations that pull these messages off NSQ and actually send the emails. This also implements, for testing purposes, a version that just echoes the Login Value and the Verification code to stdout.

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