109 lines
5.3 KiB
Go
109 lines
5.3 KiB
Go
package health
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
healthDomain "proxy-pool/internal/domain/health"
|
|
proxyDomain "proxy-pool/internal/domain/proxy"
|
|
)
|
|
|
|
func TestMemoryTaskBrokerClaimsSupportedTasksAndFencesObservations(t *testing.T) {
|
|
now := time.Date(2026, 8, 1, 9, 0, 0, 0, time.UTC)
|
|
broker, err := NewMemoryTaskBroker(MemoryTaskBrokerOptions{
|
|
LeaseTTL: time.Minute,
|
|
Now: func() time.Time { return now },
|
|
Material: TaskMaterialResolverFunc(func(_ context.Context, candidate Candidate) (TaskMaterial, error) {
|
|
return TaskMaterial{
|
|
Protocol: proxyDomain.SchemeHTTP, Host: candidate.ProxyID + ".example", Port: 8080,
|
|
SecretRef: "cred-" + candidate.ProxyID, CredentialVersion: "v1", Username: "user", Password: "secret",
|
|
}, nil
|
|
}),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("NewMemoryTaskBroker(): %v", err)
|
|
}
|
|
plans := []PlannedTask{
|
|
{Candidate: Candidate{ProxyID: "basic", State: proxyDomain.StateFetched, Level: healthDomain.LevelBasic, DueAt: now}, Deadline: now.Add(10 * time.Second), Attempts: 2},
|
|
{Candidate: Candidate{ProxyID: "target", State: proxyDomain.StateAvailable, Level: healthDomain.LevelTarget, RoutingName: "route-a", TargetURL: "https://target.example/check", DueAt: now}, Deadline: now.Add(10 * time.Second), Attempts: 2},
|
|
}
|
|
if offered, err := broker.Offer(context.Background(), plans); err != nil || offered != 2 {
|
|
t.Fatalf("Offer() = (%d, %v)", offered, err)
|
|
}
|
|
claimed, err := broker.Claim(context.Background(), TaskClaim{
|
|
CheckerID: "checker-a", InstanceID: "instance-a", MaxInFlight: 2, SupportedLevels: []healthDomain.Level{healthDomain.LevelBasic},
|
|
})
|
|
if err != nil || len(claimed) != 1 || claimed[0].ProxyID != "basic" || claimed[0].Password != "secret" {
|
|
t.Fatalf("Claim() = (%+v, %v)", claimed, err)
|
|
}
|
|
observation := healthDomain.Observation{
|
|
TaskID: claimed[0].TaskID, ProxyID: "basic", Level: healthDomain.LevelBasic,
|
|
Success: true, Latency: time.Millisecond, ObservedAt: now.Add(time.Second),
|
|
}
|
|
if err := broker.AuthorizeObservation(context.Background(), "checker-b", observation, now.Add(time.Second)); !errors.Is(err, ErrTaskLeaseNotOwned) {
|
|
t.Fatalf("AuthorizeObservation(other checker) error = %v, want ErrTaskLeaseNotOwned", err)
|
|
}
|
|
if err := broker.AuthorizeObservation(context.Background(), "checker-a", observation, now.Add(time.Second)); err != nil {
|
|
t.Fatalf("AuthorizeObservation(owner): %v", err)
|
|
}
|
|
if err := broker.CompleteObservation(context.Background(), "checker-a", observation, now.Add(time.Second)); err != nil {
|
|
t.Fatalf("CompleteObservation(): %v", err)
|
|
}
|
|
if err := broker.AuthorizeObservation(context.Background(), "checker-a", observation, now.Add(2*time.Second)); err != nil {
|
|
t.Fatalf("AuthorizeObservation(replay): %v", err)
|
|
}
|
|
}
|
|
|
|
func TestMemoryTaskBrokerReclaimsExpiredLeaseWithoutExceedingCheckerLimit(t *testing.T) {
|
|
now := time.Date(2026, 8, 1, 10, 0, 0, 0, time.UTC)
|
|
broker, err := NewMemoryTaskBroker(MemoryTaskBrokerOptions{
|
|
LeaseTTL: time.Second,
|
|
Now: func() time.Time { return now },
|
|
Material: TaskMaterialResolverFunc(func(context.Context, Candidate) (TaskMaterial, error) {
|
|
return TaskMaterial{Protocol: proxyDomain.SchemeHTTP, Host: "proxy.example", Port: 8080}, nil
|
|
}),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("NewMemoryTaskBroker(): %v", err)
|
|
}
|
|
plan := PlannedTask{Candidate: Candidate{ProxyID: "proxy-a", State: proxyDomain.StateFetched, Level: healthDomain.LevelBasic, DueAt: now}, Deadline: now.Add(time.Minute), Attempts: 1}
|
|
if _, err := broker.Offer(context.Background(), []PlannedTask{plan}); err != nil {
|
|
t.Fatalf("Offer(): %v", err)
|
|
}
|
|
first, err := broker.Claim(context.Background(), TaskClaim{CheckerID: "checker-a", InstanceID: "instance-a", MaxInFlight: 1, SupportedLevels: []healthDomain.Level{healthDomain.LevelBasic}})
|
|
if err != nil || len(first) != 1 {
|
|
t.Fatalf("first Claim() = (%+v, %v)", first, err)
|
|
}
|
|
second, err := broker.Claim(context.Background(), TaskClaim{CheckerID: "checker-a", InstanceID: "instance-a", MaxInFlight: 1, SupportedLevels: []healthDomain.Level{healthDomain.LevelBasic}})
|
|
if err != nil || len(second) != 0 {
|
|
t.Fatalf("second Claim() = (%+v, %v)", second, err)
|
|
}
|
|
now = now.Add(2 * time.Second)
|
|
reclaimed, err := broker.Claim(context.Background(), TaskClaim{CheckerID: "checker-b", InstanceID: "instance-b", MaxInFlight: 1, SupportedLevels: []healthDomain.Level{healthDomain.LevelBasic}})
|
|
if err != nil || len(reclaimed) != 1 || reclaimed[0].TaskID != first[0].TaskID {
|
|
t.Fatalf("reclaimed Claim() = (%+v, %v)", reclaimed, err)
|
|
}
|
|
}
|
|
|
|
func TestMemoryTaskBrokerRejectsTerminalProxyState(t *testing.T) {
|
|
now := time.Date(2026, 8, 1, 11, 0, 0, 0, time.UTC)
|
|
broker, err := NewMemoryTaskBroker(MemoryTaskBrokerOptions{
|
|
LeaseTTL: time.Second, Now: func() time.Time { return now },
|
|
Material: TaskMaterialResolverFunc(func(context.Context, Candidate) (TaskMaterial, error) {
|
|
return TaskMaterial{Protocol: proxyDomain.SchemeHTTP, Host: "proxy.example", Port: 8080}, nil
|
|
}),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("NewMemoryTaskBroker(): %v", err)
|
|
}
|
|
_, err = broker.Offer(context.Background(), []PlannedTask{{
|
|
Candidate: Candidate{ProxyID: "proxy-a", State: proxyDomain.StateExtracted, Level: healthDomain.LevelBasic, DueAt: now},
|
|
Deadline: now.Add(time.Second), Attempts: 1,
|
|
}})
|
|
if !errors.Is(err, ErrInvalidLeasedTask) {
|
|
t.Fatalf("Offer(terminal state) error = %v, want ErrInvalidLeasedTask", err)
|
|
}
|
|
}
|