package auth import ( "context" "fmt" "sync" "testing" ) func TestConcurrentChallengesSameIdentityDoNotOverwrite(t *testing.T) { m := New(nil, "test-secret") ctx := context.Background() c1, err := m.NewChallenge(ctx, "client-a") if err != nil { t.Fatal(err) } c2, err := m.NewChallenge(ctx, "client-a") if err != nil { t.Fatal(err) } if c1 == c2 { t.Fatal("expected unique challenges") } if err := m.ConsumeChallenge(ctx, "client-a", c1); err != nil { t.Fatalf("first challenge should remain valid: %v", err) } if err := m.ConsumeChallenge(ctx, "client-a", c2); err != nil { t.Fatalf("second challenge should remain valid: %v", err) } } func TestChallengeIsOneShotAndIdentityBound(t *testing.T) { m := New(nil, "test-secret") ctx := context.Background() c, err := m.NewChallenge(ctx, "client-a") if err != nil { t.Fatal(err) } if err := m.ConsumeChallenge(ctx, "client-b", c); err == nil { t.Fatal("challenge must not be consumable by another identity") } if err := m.ConsumeChallenge(ctx, "client-a", c); err == nil { t.Fatal("challenge must be one-shot after a consume attempt") } } func TestManyConcurrentChallenges(t *testing.T) { m := New(nil, "test-secret") ctx := context.Background() const n = 64 type item struct{ cid, challenge string } out := make(chan item, n) errCh := make(chan error, n) var wg sync.WaitGroup for i := 0; i < n; i++ { i := i wg.Add(1) go func() { defer wg.Done() cid := fmt.Sprintf("client-%d", i) c, err := m.NewChallenge(ctx, cid) if err != nil { errCh <- err return } out <- item{cid: cid, challenge: c} }() } wg.Wait() close(out) close(errCh) for err := range errCh { t.Fatal(err) } for x := range out { if err := m.ConsumeChallenge(ctx, x.cid, x.challenge); err != nil { t.Fatalf("consume %s: %v", x.cid, err) } } } func TestChallengeProofOfWorkForNewIdentity(t *testing.T) { m := New(nil, "test-secret") ctx := context.Background() const cid = "client-pow" challenge, err := m.NewChallengeWithProof(ctx, cid, 8) if err != nil { t.Fatal(err) } counter := "" for i := 0; i < 100000; i++ { candidate := fmt.Sprint(i) if verifyChallengeProof(challenge, cid, candidate, 8) { counter = candidate break } } if counter == "" { t.Fatal("failed to solve small test proof") } if err := m.ConsumeChallengeWithProof(ctx, cid, challenge, counter); err != nil { t.Fatalf("valid proof rejected: %v", err) } }