auth

Paddy 2015-06-29 Parent:8ecb60d29b0d

175:aa14e29b666f Go to Latest

auth/profile_test.go

Create Docker image for authd. Create a Dockerfile for authd, which will wrap the compiled Go binary up into a tiny little Docker image. Create an authd/build-docker.sh script that will build the statically-linked binary in a Docker container, so the authd Docker image can use it. We had to include ca-certificates.crt in the Dockerfile, as well, so we could communicate over SSL with things. A wrapper.sh file is included that will pull the JWT_SECRET environment variable out of a kubernetes secrets file, which is a handy wrapper to have. Finally, we added the authd/docker-authd binary to the .hgignore.

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