package registry import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "time" "github.com/beckn-one/beckn-onix/pkg/model" ) // TestValidate ensures the config validation logic works correctly. func TestValidate(t *testing.T) { t.Parallel() testCases := []struct { name string config *Config expectedErr string }{ { name: "should return error for nil config", config: nil, expectedErr: "registry config cannot be nil", }, { name: "should return error for empty URL", config: &Config{URL: ""}, expectedErr: "registry URL cannot be empty", }, { name: "should succeed for valid config", config: &Config{URL: "http://localhost:8080"}, expectedErr: "", }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { err := validate(tc.config) if tc.expectedErr != "" { if err == nil { t.Fatalf("expected an error but got none") } if err.Error() != tc.expectedErr { t.Errorf("expected error message '%s', but got '%s'", tc.expectedErr, err.Error()) } } else { if err != nil { t.Fatalf("expected no error, but got: %v", err) } } }) } } // TestNew tests the constructor for the RegistryClient. func TestNew(t *testing.T) { t.Parallel() t.Run("should fail with invalid config", func(t *testing.T) { _, _, err := New(context.Background(), &Config{URL: ""}) if err == nil { t.Fatal("expected an error for invalid config but got none") } }) t.Run("should succeed with valid config and set defaults", func(t *testing.T) { cfg := &Config{URL: "http://test.com"} client, closer, err := New(context.Background(), cfg) if err != nil { t.Fatalf("expected no error, but got: %v", err) } if client == nil { t.Fatal("expected client to be non-nil") } if closer == nil { t.Fatal("expected closer to be non-nil") } // Check if default retry settings are applied (go-retryablehttp defaults) if client.client.RetryMax != 4 { t.Errorf("expected default RetryMax of 4, but got %d", client.client.RetryMax) } }) t.Run("should apply custom retry settings", func(t *testing.T) { cfg := &Config{ URL: "http://test.com", RetryMax: 10, RetryWaitMin: 100 * time.Millisecond, RetryWaitMax: 1 * time.Second, } client, _, err := New(context.Background(), cfg) if err != nil { t.Fatalf("expected no error, but got: %v", err) } if client.client.RetryMax != cfg.RetryMax { t.Errorf("expected RetryMax to be %d, but got %d", cfg.RetryMax, client.client.RetryMax) } if client.client.RetryWaitMin != cfg.RetryWaitMin { t.Errorf("expected RetryWaitMin to be %v, but got %v", cfg.RetryWaitMin, client.client.RetryWaitMin) } if client.client.RetryWaitMax != cfg.RetryWaitMax { t.Errorf("expected RetryWaitMax to be %v, but got %v", cfg.RetryWaitMax, client.client.RetryWaitMax) } }) } // TestRegistryClient_Lookup tests the Lookup method. func TestRegistryClient_Lookup(t *testing.T) { t.Parallel() t.Run("should succeed and unmarshal response", func(t *testing.T) { expectedSubs := []model.Subscription{ { KeyID: "test-key", SigningPublicKey: "test-signing-key", EncrPublicKey: "test-encryption-key", ValidFrom: time.Now(), ValidUntil: time.Now().Add(24 * time.Hour), Status: "SUBSCRIBED", }, { KeyID: "test-key-2", SigningPublicKey: "test-signing-key-2", EncrPublicKey: "test-encryption-key-2", ValidFrom: time.Now(), ValidUntil: time.Now().Add(48 * time.Hour), Status: "SUBSCRIBED", }, } server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/lookup" { t.Errorf("expected path '/lookup', got '%s'", r.URL.Path) } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) if err := json.NewEncoder(w).Encode(expectedSubs); err != nil { t.Fatalf("failed to write response: %v", err) } })) defer server.Close() client, closer, err := New(context.Background(), &Config{URL: server.URL}) if err != nil { t.Fatalf("failed to create client: %v", err) } defer closer() results, err := client.Lookup(context.Background(), &model.Subscription{}) if err != nil { t.Fatalf("lookup failed: %v", err) } if len(results) != len(expectedSubs) { t.Fatalf("expected %d results, but got %d", len(expectedSubs), len(results)) } if results[0].SubscriberID != expectedSubs[0].SubscriberID { t.Errorf("expected subscriber ID '%s', got '%s'", expectedSubs[0].SubscriberID, results[0].SubscriberID) } }) t.Run("should fail on non-200 status", func(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusBadRequest) })) defer server.Close() client, closer, err := New(context.Background(), &Config{URL: server.URL}) if err != nil { t.Fatalf("failed to create client: %v", err) } defer closer() _, err = client.Lookup(context.Background(), &model.Subscription{}) if err == nil { t.Fatal("expected an error but got none") } }) t.Run("should fail on bad JSON response", func(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) fmt.Fprint(w, `[{"subscriber_id": "bad-json"`) // Malformed JSON })) defer server.Close() client, closer, err := New(context.Background(), &Config{URL: server.URL, RetryMax: 1}) if err != nil { t.Fatalf("failed to create client: %v", err) } defer closer() _, err = client.Lookup(context.Background(), &model.Subscription{}) if err == nil { t.Fatal("expected an unmarshaling error but got none") } }) }