package db import ( "errors" "testing" "time" ) func TestSetFIPAssociatedStampsTimestamp(t *testing.T) { d, ctx := newTestDB(t) if err := d.SeedQueue(ctx, []string{"1.2.3.4"}); err != nil { t.Fatalf("seed queue: %v", err) } if err := d.AdminCreateValidator(ctx, "validator-1", "port-1"); err != nil { t.Fatalf("create validator: %v", err) } claimed, err := d.ClaimNextQueued(ctx, "validator-1", time.Minute) if err != nil || claimed == nil { t.Fatalf("claim: item=%+v err=%v", claimed, err) } if claimed.FIPAssociatedAt != nil { t.Fatalf("expected FIPAssociatedAt nil before association, got %v", claimed.FIPAssociatedAt) } before := Now() if err := d.SetFIPAssociated(ctx, claimed.ID, "fip-1", time.Minute); err != nil { t.Fatalf("set fip associated: %v", err) } item, err := d.GetIP(ctx, claimed.ID) if err != nil { t.Fatalf("get ip: %v", err) } if item.FIPAssociatedAt == nil { t.Fatalf("expected FIPAssociatedAt to be set") } if item.FIPAssociatedAt.Before(before) { t.Fatalf("expected FIPAssociatedAt >= %v, got %v", before, *item.FIPAssociatedAt) } } func TestMarkFIPOccupiedIsTerminalAndFreesValidator(t *testing.T) { d, ctx := newTestDB(t) if err := d.SeedQueue(ctx, []string{"1.2.3.4"}); err != nil { t.Fatalf("seed queue: %v", err) } if err := d.AdminCreateValidator(ctx, "validator-1", "port-1"); err != nil { t.Fatalf("create validator: %v", err) } claimed, err := d.ClaimNextQueued(ctx, "validator-1", time.Minute) if err != nil || claimed == nil { t.Fatalf("claim: item=%+v err=%v", claimed, err) } if err := d.MarkFIPOccupied(ctx, claimed.ID, "validator-1"); err != nil { t.Fatalf("mark fip occupied: %v", err) } item, err := d.GetIP(ctx, claimed.ID) if err != nil { t.Fatalf("get ip: %v", err) } if item.State != IPOccupied { t.Fatalf("expected occupied, got state=%s", item.State) } if item.OwnerValidatorID != nil { t.Fatalf("expected owner cleared, got %v", *item.OwnerValidatorID) } if item.FIPID != "" { t.Fatalf("expected fip_id cleared, got %q", item.FIPID) } if item.LeaseExpiresAt != nil { t.Fatalf("expected lease cleared, got %v", item.LeaseExpiresAt) } v, err := d.GetValidator(ctx, "validator-1") if err != nil { t.Fatalf("get validator: %v", err) } if v.State != ValidatorIdle || v.CurrentIPID != nil { t.Fatalf("expected validator freed to idle, got state=%s current_ip=%v", v.State, v.CurrentIPID) } } func TestSubmitIPsResubmitsOccupiedAddress(t *testing.T) { d, ctx := newTestDB(t) if err := d.SeedQueue(ctx, []string{"1.2.3.4"}); err != nil { t.Fatalf("seed queue: %v", err) } if err := d.AdminCreateValidator(ctx, "validator-1", "port-1"); err != nil { t.Fatalf("create validator: %v", err) } claimed, err := d.ClaimNextQueued(ctx, "validator-1", time.Minute) if err != nil || claimed == nil { t.Fatalf("claim: item=%+v err=%v", claimed, err) } if err := d.MarkFIPOccupied(ctx, claimed.ID, "validator-1"); err != nil { t.Fatalf("mark fip occupied: %v", err) } result, err := d.SubmitIPs(ctx, []string{"1.2.3.4"}) if err != nil { t.Fatalf("submit ips: %v", err) } if len(result.Requeued) != 1 || result.Requeued[0] != "1.2.3.4" { t.Fatalf("expected address requeued, got %+v", result) } if len(result.SkippedInProgress) != 0 { t.Fatalf("expected nothing skipped as in-progress, got %+v", result.SkippedInProgress) } item, err := d.GetIP(ctx, claimed.ID) if err != nil { t.Fatalf("get ip: %v", err) } if item.State != IPQueued { t.Fatalf("expected queued after resubmit, got state=%s", item.State) } } func TestCancelIPRejectsAlreadyOccupied(t *testing.T) { d, ctx := newTestDB(t) if err := d.SeedQueue(ctx, []string{"1.2.3.4"}); err != nil { t.Fatalf("seed queue: %v", err) } if err := d.AdminCreateValidator(ctx, "validator-1", "port-1"); err != nil { t.Fatalf("create validator: %v", err) } claimed, err := d.ClaimNextQueued(ctx, "validator-1", time.Minute) if err != nil || claimed == nil { t.Fatalf("claim: item=%+v err=%v", claimed, err) } if err := d.MarkFIPOccupied(ctx, claimed.ID, "validator-1"); err != nil { t.Fatalf("mark fip occupied: %v", err) } err = d.CancelIP(ctx, claimed.ID) if !errors.Is(err, ErrInvalidState) { t.Fatalf("expected ErrInvalidState cancelling an occupied ip, got %v", err) } } func TestRequeueClearsFIPAssociatedAt(t *testing.T) { d, ctx := newTestDB(t) if err := d.SeedQueue(ctx, []string{"1.2.3.4"}); err != nil { t.Fatalf("seed queue: %v", err) } if err := d.AdminCreateValidator(ctx, "validator-1", "port-1"); err != nil { t.Fatalf("create validator: %v", err) } claimed, err := d.ClaimNextQueued(ctx, "validator-1", time.Minute) if err != nil || claimed == nil { t.Fatalf("claim: item=%+v err=%v", claimed, err) } if err := d.SetFIPAssociated(ctx, claimed.ID, "fip-1", time.Minute); err != nil { t.Fatalf("set fip associated: %v", err) } if err := d.RequeueOrFail(ctx, claimed.ID, "validator-1", 3); err != nil { t.Fatalf("requeue or fail: %v", err) } item, err := d.GetIP(ctx, claimed.ID) if err != nil { t.Fatalf("get ip after requeue: %v", err) } if item.State != IPQueued { t.Fatalf("expected requeued, got state=%s", item.State) } if item.FIPAssociatedAt != nil { t.Fatalf("expected FIPAssociatedAt cleared on requeue, got %v", item.FIPAssociatedAt) } }