package auth import ( "strings" "testing" ) func TestHashAndVerify(t *testing.T) { t.Parallel() pw := "correct horse battery staple" h, err := HashPassword(pw) if err != nil { t.Fatalf("hash: %v", err) } if !strings.HasPrefix(h, "$argon2id$") { t.Errorf("encoded form should start $argon2id$, got %q", h) } if err := VerifyPassword(h, pw); err != nil { t.Errorf("verify: %v", err) } if err := VerifyPassword(h, "wrong"); err == nil { t.Error("verify with wrong password should fail") } } func TestEachHashIsUnique(t *testing.T) { t.Parallel() // Same password hashed twice → different encoded strings (different // salts). If this fails the salt is deterministic. a, _ := HashPassword("hunter2") b, _ := HashPassword("hunter2") if a == b { t.Fatal("two hashes of the same password collided — non-random salt?") } } func TestVerifyRejectsMalformed(t *testing.T) { t.Parallel() cases := []string{ "", "not-a-hash", "$argon2i$v=19$m=64,t=3,p=4$AAAA$BBBB", // wrong variant "$argon2id$", // truncated "$argon2id$v=99$m=64,t=3,p=4$AAAA$BBBB", // bad version } for _, c := range cases { if err := VerifyPassword(c, "anything"); err == nil { t.Errorf("should reject malformed hash %q", c) } } } func TestNewTokenUnique(t *testing.T) { t.Parallel() a, err := NewToken() if err != nil { t.Fatalf("token: %v", err) } b, _ := NewToken() if a == b { t.Fatal("two tokens collided — broken randomness") } if len(a) < 40 { t.Errorf("token suspiciously short: %q (%d bytes)", a, len(a)) } } func TestHashTokenStable(t *testing.T) { t.Parallel() // Same input → same hash. This is not a security property, just a // sanity check that we're using a regular hash not a salted one. h1 := HashToken("foo") h2 := HashToken("foo") if h1 != h2 { t.Errorf("HashToken not deterministic: %q vs %q", h1, h2) } if len(h1) != 64 { // sha256 hex t.Errorf("expected 64-char hex hash, got %d", len(h1)) } }