package openstack import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "time" "github.com/gophercloud/gophercloud/v2" ) // TestClientListFreeFloatingIPsAgainstFakeNeutron drives the real Client // against an httptest server: marker pagination, the `fields` projection, a // transient 503 that is retried and an empty port_id meaning "free". func TestClientListFreeFloatingIPsAgainstFakeNeutron(t *testing.T) { const total = 5 var requests int failedOnce := false srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { requests++ q := r.URL.Query() if got := q["fields"]; len(got) != 4 { t.Errorf("expected 4 fields params, got %v", got) } if q.Get("limit") != "2" { t.Errorf("expected limit=2, got %q", q.Get("limit")) } if q.Get("marker") == "id-1" && !failedOnce { failedOnce = true http.Error(w, "busy", http.StatusServiceUnavailable) return } start := 0 if m := q.Get("marker"); m != "" { fmt.Sscanf(m, "id-%d", &start) start++ } type fip struct { ID string `json:"id"` Addr string `json:"floating_ip_address"` PortID *string `json:"port_id"` TenantID string `json:"tenant_id"` } var items []fip for i := start; i < total && len(items) < 2; i++ { f := fip{ID: fmt.Sprintf("id-%d", i), Addr: fmt.Sprintf("203.0.113.%d", i+1), TenantID: "p"} if i == 1 { p := "port-1" f.PortID = &p } items = append(items, f) } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]any{"floatingips": items}) })) defer srv.Close() c := &Client{ networking: &gophercloud.ServiceClient{ ProviderClient: &gophercloud.ProviderClient{HTTPClient: *srv.Client()}, Endpoint: srv.URL + "/", ResourceBase: srv.URL + "/v2.0/", }, retry: pageRetry{Retries: 3, Sleep: func(context.Context, time.Duration) error { return nil }}, } var all []FloatingIP pages, err := c.ListFreeFloatingIPs(context.Background(), 2, func(p []FloatingIP) error { all = append(all, p...) return nil }) if err != nil { t.Fatalf("list: %v", err) } if pages != 3 || len(all) != total { t.Fatalf("expected 3 pages / 5 fips, got %d / %d", pages, len(all)) } if all[1].PortID != "port-1" || all[0].PortID != "" || all[4].Address != "203.0.113.5" { t.Fatalf("unexpected mapping: %+v", all) } if requests != 4 { // 3 pages + 1 retried 503 t.Fatalf("expected 4 requests, got %d", requests) } }