proxy-pool/internal/adapters/redisactivity/health_tasks_integration_test.go
youfak d985f211d5
Some checks are pending
ci / proto (push) Waiting to run
ci / test (ubuntu-latest) (push) Waiting to run
ci / test (windows-latest) (push) Waiting to run
ci / race (push) Waiting to run
ci / integration (push) Waiting to run
fix: clear health tasks with proxy lifecycle
2026-07-31 22:12:07 +08:00

126 lines
5.1 KiB
Go

//go:build integration
package redisactivity
import (
"context"
"errors"
"testing"
"time"
controllerHealth "proxy-pool/internal/controller/health"
"proxy-pool/internal/domain/activitypool"
healthDomain "proxy-pool/internal/domain/health"
proxyDomain "proxy-pool/internal/domain/proxy"
)
func TestRedisHealthTasksLeaseAndRescheduleBasicChecks(t *testing.T) {
fixture := newRedisTestFixture(t)
ctx := context.Background()
now := time.Now().UTC()
if _, err := fixture.Adapter.UpsertFetched(ctx, "provider-a", activitypool.FetchedBatch{
ObservedAt: now, ConfiguredTTL: time.Minute, MaxSize: 1,
Proxies: []proxyDomain.Proxy{testProxy("proxy-a", "192.0.2.10")},
}); err != nil {
t.Fatalf("UpsertFetched(): %v", err)
}
candidates, err := fixture.Adapter.DueCandidates(ctx, now, 1)
if err != nil || len(candidates) != 1 || candidates[0].ProxyID != "proxy-a" || candidates[0].State != proxyDomain.StateFetched {
t.Fatalf("DueCandidates() = (%+v, %v)", candidates, err)
}
planner, err := controllerHealth.NewPlanner(controllerHealth.SchedulePolicy{
Interval: 30 * time.Second, MaxInFlight: 1, Timeout: 5 * time.Second, MaxAttempts: 1,
})
if err != nil {
t.Fatalf("newHealthTaskPlanner(): %v", err)
}
plannedAt := now
plans, err := planner.Plan(plannedAt, 0, 1, candidates)
if err != nil || len(plans) != 1 {
t.Fatalf("Plan() = (%+v, %v)", plans, err)
}
if offered, err := fixture.Adapter.Offer(ctx, plans); err != nil || offered != 1 {
t.Fatalf("Offer() = (%d, %v)", offered, err)
}
claimed, err := fixture.Adapter.Claim(ctx, healthDomain.TaskClaim{
CheckerID: "checker-a", InstanceID: "instance-a", MaxInFlight: 1,
SupportedLevels: []healthDomain.Level{healthDomain.LevelBasic},
})
if err != nil || len(claimed) != 1 || claimed[0].ProxyID != "proxy-a" || claimed[0].LeaseToken == "" {
t.Fatalf("Claim() = (%+v, %v)", claimed, err)
}
observation := healthDomain.Observation{
TaskID: claimed[0].TaskID, ProxyID: claimed[0].ProxyID, Level: healthDomain.LevelBasic,
Success: true, Latency: time.Millisecond, ObservedAt: time.Now().UTC(),
}
if err := fixture.Adapter.AuthorizeObservation(ctx, "checker-a", claimed[0].LeaseToken, observation, time.Now().UTC()); err != nil {
t.Fatalf("AuthorizeObservation(): %v", err)
}
updated, err := fixture.Adapter.ApplyGlobalObservation(ctx, activitypool.GlobalHealthCommand{
Observation: observation, MaxConsecutiveFailures: 2,
})
if err != nil || updated.State != proxyDomain.StateAvailable {
t.Fatalf("ApplyGlobalObservation() = (%+v, %v)", updated, err)
}
if err := fixture.Adapter.CompleteObservation(ctx, "checker-a", claimed[0].LeaseToken, observation, time.Now().UTC()); err != nil {
t.Fatalf("CompleteObservation(): %v", err)
}
next, err := fixture.Adapter.DueCandidates(ctx, plans[0].NextDue, 1)
if err != nil || len(next) != 1 || next[0].ProxyID != "proxy-a" || next[0].State != proxyDomain.StateAvailable {
t.Fatalf("DueCandidates(next) = (%+v, %v)", next, err)
}
}
func TestRedisHealthTaskSweepReleasesLeaseAndUpstreamCapacity(t *testing.T) {
fixture := newRedisTestFixture(t)
ctx := context.Background()
now := time.Now().UTC()
if _, err := fixture.Adapter.UpsertFetched(ctx, "provider-a", activitypool.FetchedBatch{
ObservedAt: now, ConfiguredTTL: time.Second, MaxSize: 1,
Proxies: []proxyDomain.Proxy{testProxy("proxy-a", "192.0.2.10")},
}); err != nil {
t.Fatalf("UpsertFetched(): %v", err)
}
candidates, err := fixture.Adapter.DueCandidatesForUpstream(ctx, "provider-a", now, 1)
if err != nil || len(candidates) != 1 {
t.Fatalf("DueCandidatesForUpstream() = (%+v, %v)", candidates, err)
}
planner, err := controllerHealth.NewPlanner(controllerHealth.SchedulePolicy{
Interval: 30 * time.Second, MaxInFlight: 1, Timeout: 10 * time.Second, MaxAttempts: 1,
})
if err != nil {
t.Fatalf("NewPlanner(): %v", err)
}
plans, err := planner.Plan(now, 0, 1, candidates)
if err != nil || len(plans) != 1 {
t.Fatalf("Plan() = (%+v, %v)", plans, err)
}
if offered, err := fixture.Adapter.Offer(ctx, plans); err != nil || offered != 1 {
t.Fatalf("Offer() = (%d, %v)", offered, err)
}
claimed, err := fixture.Adapter.Claim(ctx, healthDomain.TaskClaim{
CheckerID: "checker-a", InstanceID: "instance-a", MaxInFlight: 1,
SupportedLevels: []healthDomain.Level{healthDomain.LevelBasic},
})
if err != nil || len(claimed) != 1 {
t.Fatalf("Claim() = (%+v, %v)", claimed, err)
}
expiredAt := now.Add(2 * time.Second)
if removed, err := fixture.Adapter.SweepExpired(ctx, expiredAt, 1); err != nil || removed != 1 {
t.Fatalf("SweepExpired() = (%d, %v)", removed, err)
}
if inFlight, err := fixture.Adapter.InFlightForUpstream(ctx, "provider-a", expiredAt); err != nil || inFlight != 0 {
t.Fatalf("InFlightForUpstream() = (%d, %v)", inFlight, err)
}
observation := healthDomain.Observation{
TaskID: claimed[0].TaskID, ProxyID: claimed[0].ProxyID, Level: healthDomain.LevelBasic,
Success: true, Latency: time.Millisecond, ObservedAt: now,
}
if err := fixture.Adapter.AuthorizeObservation(ctx, "checker-a", claimed[0].LeaseToken, observation, expiredAt); !errors.Is(err, healthDomain.ErrTaskNotFound) {
t.Fatalf("AuthorizeObservation(after sweep) error = %v, want ErrTaskNotFound", err)
}
}