From 19fd4bb7105e63a09d226d3a9c593b97c37d144b Mon Sep 17 00:00:00 2001 From: giogam <151543+giogam@users.noreply.github.com> Date: Thu, 30 Oct 2025 12:10:28 +0100 Subject: [PATCH 1/5] fix(catalog): updates errors in remote implementation --- datastore/catalog/remote/address_ref_store.go | 6 ++++++ datastore/catalog/remote/chain_metadata_store.go | 6 ++++++ datastore/catalog/remote/contract_metadata_store.go | 6 ++++++ datastore/catalog/remote/env_metadata_store.go | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/datastore/catalog/remote/address_ref_store.go b/datastore/catalog/remote/address_ref_store.go index 242496fa3..dac28e092 100644 --- a/datastore/catalog/remote/address_ref_store.go +++ b/datastore/catalog/remote/address_ref_store.go @@ -7,6 +7,7 @@ import ( "github.com/Masterminds/semver/v3" "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" @@ -157,6 +158,11 @@ func (s *catalogAddressRefStore) Fetch(_ context.Context) ([]datastore.AddressRe // Check for errors in the response if err := parseResponseStatus(response.Status); err != nil { + st, _ := status.FromError(err) + if st.Code() == codes.NotFound { + return nil, datastore.ErrAddressRefNotFound + } + return nil, fmt.Errorf("fetch address refs failed: %w", err) } diff --git a/datastore/catalog/remote/chain_metadata_store.go b/datastore/catalog/remote/chain_metadata_store.go index cd9c65aea..355741fb0 100644 --- a/datastore/catalog/remote/chain_metadata_store.go +++ b/datastore/catalog/remote/chain_metadata_store.go @@ -8,6 +8,7 @@ import ( "sync" "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" @@ -210,6 +211,11 @@ func (s *catalogChainMetadataStore) Fetch(_ context.Context) ([]datastore.ChainM // Check for errors in the response if err := parseResponseStatus(resp.Status); err != nil { + st, _ := status.FromError(err) + if st.Code() == codes.NotFound { + return nil, datastore.ErrChainMetadataNotFound + } + return nil, fmt.Errorf("fetch chain metadata failed: %w", err) } diff --git a/datastore/catalog/remote/contract_metadata_store.go b/datastore/catalog/remote/contract_metadata_store.go index b56ddf823..4367549d6 100644 --- a/datastore/catalog/remote/contract_metadata_store.go +++ b/datastore/catalog/remote/contract_metadata_store.go @@ -9,6 +9,7 @@ import ( "sync" "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" @@ -214,6 +215,11 @@ func (s *catalogContractMetadataStore) Fetch(_ context.Context) ([]datastore.Con // Check for errors in the response if statusErr := parseResponseStatus(resp.Status); statusErr != nil { + st, _ := status.FromError(statusErr) + if st.Code() == codes.NotFound { + return nil, datastore.ErrContractMetadataNotFound + } + return nil, fmt.Errorf("fetch contract metadata failed: %w", statusErr) } diff --git a/datastore/catalog/remote/env_metadata_store.go b/datastore/catalog/remote/env_metadata_store.go index f842c5f10..05c7d553e 100644 --- a/datastore/catalog/remote/env_metadata_store.go +++ b/datastore/catalog/remote/env_metadata_store.go @@ -8,6 +8,7 @@ import ( "sync" "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" @@ -131,6 +132,11 @@ func (s *catalogEnvMetadataStore) get(ignoreTransaction bool) (datastore.EnvMeta } if sendErr := stream.Send(findReq); sendErr != nil { + st, _ := status.FromError(sendErr) + if st.Code() == codes.NotFound { + return datastore.EnvMetadata{}, datastore.ErrEnvMetadataNotSet + } + return datastore.EnvMetadata{}, fmt.Errorf("failed to send find request: %w", sendErr) } From 0484179edf7df48562dd43134d4946c2350ea7f1 Mon Sep 17 00:00:00 2001 From: Giorgio Gambino <151543+giogam@users.noreply.github.com> Date: Thu, 30 Oct 2025 11:13:58 +0000 Subject: [PATCH 2/5] Create tough-starfishes-punch.md --- .changeset/tough-starfishes-punch.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/tough-starfishes-punch.md diff --git a/.changeset/tough-starfishes-punch.md b/.changeset/tough-starfishes-punch.md new file mode 100644 index 000000000..13f3b392d --- /dev/null +++ b/.changeset/tough-starfishes-punch.md @@ -0,0 +1,5 @@ +--- +"chainlink-deployments-framework": patch +--- + +fix(catalog): updates errors in remote implementation From eb4b46192c9369d566a6da9f0348679ce0495246 Mon Sep 17 00:00:00 2001 From: giogam <151543+giogam@users.noreply.github.com> Date: Thu, 30 Oct 2025 14:47:09 +0100 Subject: [PATCH 3/5] fix(catalog): updates errors in remote implementation --- .../catalog/remote/address_ref_store_test.go | 80 +++++++++++++------ .../remote/chain_metadata_store_test.go | 80 +++++++++++++------ .../remote/contract_metadata_store_test.go | 80 +++++++++++++------ .../catalog/remote/env_metadata_store_test.go | 58 +++++++------- 4 files changed, 198 insertions(+), 100 deletions(-) diff --git a/datastore/catalog/remote/address_ref_store_test.go b/datastore/catalog/remote/address_ref_store_test.go index eab7f43f1..5c6a0e927 100644 --- a/datastore/catalog/remote/address_ref_store_test.go +++ b/datastore/catalog/remote/address_ref_store_test.go @@ -59,7 +59,7 @@ func TestCatalogAddressRefStore_Get(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestStore(t) + store := setupTestStore(t, "test-domain", "catalog_testing") key := tt.setup(store) @@ -116,7 +116,7 @@ func TestCatalogAddressRefStore_Add(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestStore(t) + store := setupTestStore(t, "test-domain", "catalog_testing") addressRef := tt.setup(store) @@ -197,7 +197,7 @@ func TestCatalogAddressRefStore_Update(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestStore(t) + store := setupTestStore(t, "test-domain", "catalog_testing") addressRef := tt.setup(store) @@ -272,7 +272,7 @@ func TestCatalogAddressRefStore_Upsert(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestStore(t) + store := setupTestStore(t, "test-domain", "catalog_testing") addressRef := tt.setup(store) @@ -288,7 +288,7 @@ func TestCatalogAddressRefStore_Upsert(t *testing.T) { func TestCatalogAddressRefStore_Delete(t *testing.T) { t.Parallel() - store := setupTestStore(t) + store := setupTestStore(t, "", "") version := semver.MustParse("1.0.0") key := datastore.NewAddressRefKey(12345, "LinkToken", version, "test") @@ -309,11 +309,17 @@ func TestCatalogAddressRefStore_FetchAndFilter(t *testing.T) { setup func(store *catalogAddressRefStore) (datastore.AddressRef, datastore.AddressRef) createFilter func(addressRef1, addressRef2 datastore.AddressRef) datastore.FilterFunc[datastore.AddressRefKey, datastore.AddressRef] minExpected int + expectError bool + errorType error + domain string + environment string verify func(t *testing.T, results []datastore.AddressRef, addressRef1, addressRef2 datastore.AddressRef) }{ { - name: "fetch_all", - operation: "fetch", + name: "fetch_all", + operation: "fetch", + domain: "test-domain", + environment: "catalog_testing", setup: func(store *catalogAddressRefStore) (datastore.AddressRef, datastore.AddressRef) { // Setup test data with unique chain selectors addressRef1 := newRandomAddressRef() @@ -354,8 +360,10 @@ func TestCatalogAddressRefStore_FetchAndFilter(t *testing.T) { }, }, { - name: "filter_by_chain_selector", - operation: "filter", + name: "filter_by_chain_selector", + operation: "filter", + domain: "test-domain", + environment: "catalog_testing", setup: func(store *catalogAddressRefStore) (datastore.AddressRef, datastore.AddressRef) { // Setup test data with unique chain selectors addressRef1 := newRandomAddressRef() @@ -390,8 +398,10 @@ func TestCatalogAddressRefStore_FetchAndFilter(t *testing.T) { }, }, { - name: "filter_by_address", - operation: "filter", + name: "filter_by_address", + operation: "filter", + domain: "test-domain", + environment: "catalog_testing", setup: func(store *catalogAddressRefStore) (datastore.AddressRef, datastore.AddressRef) { // Setup test data with unique addresses addressRef1 := newRandomAddressRef() @@ -418,8 +428,10 @@ func TestCatalogAddressRefStore_FetchAndFilter(t *testing.T) { }, }, { - name: "filter_by_contract_type", - operation: "filter", + name: "filter_by_contract_type", + operation: "filter", + domain: "test-domain", + environment: "catalog_testing", setup: func(store *catalogAddressRefStore) (datastore.AddressRef, datastore.AddressRef) { // Setup test data with different contract types addressRef1 := newRandomAddressRef() @@ -447,14 +459,27 @@ func TestCatalogAddressRefStore_FetchAndFilter(t *testing.T) { } }, }, + { + name: "fetch_not_found", + operation: "fetch", + setup: func(store *catalogAddressRefStore) (datastore.AddressRef, datastore.AddressRef) { + return datastore.AddressRef{}, datastore.AddressRef{} + }, + createFilter: nil, + minExpected: 0, + expectError: true, + errorType: datastore.ErrAddressRefNotFound, + domain: "empty-domain-for-testing", + environment: "empty-env-for-testing", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - // Create a fresh store for each test case to avoid concurrency issues - store := setupTestStore(t) + // Create a fresh store for each test case to avoid concurrency issues + store := setupTestStore(t, tt.domain, tt.environment) addressRef1, addressRef2 := tt.setup(store) var results []datastore.AddressRef @@ -473,10 +498,18 @@ func TestCatalogAddressRefStore_FetchAndFilter(t *testing.T) { } // Verify - require.NoError(t, err) - require.GreaterOrEqual(t, len(results), tt.minExpected) - if tt.verify != nil { - tt.verify(t, results, addressRef1, addressRef2) + if tt.expectError { + require.Error(t, err) + if tt.errorType != nil { + require.ErrorIs(t, err, tt.errorType) + } + require.Nil(t, results) + } else { + require.NoError(t, err) + require.GreaterOrEqual(t, len(results), tt.minExpected) + if tt.verify != nil { + tt.verify(t, results, addressRef1, addressRef2) + } } }) } @@ -576,7 +609,7 @@ func TestCatalogAddressRefStore_ConversionHelpers(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestStore(t) + store := setupTestStore(t, "test-domain", "catalog_testing") tt.test(t, store) }) @@ -584,8 +617,9 @@ func TestCatalogAddressRefStore_ConversionHelpers(t *testing.T) { } // setupTestStore creates a real gRPC client connection to a local service -func setupTestStore(t *testing.T) *catalogAddressRefStore { +func setupTestStore(t *testing.T, domain, environment string) *catalogAddressRefStore { t.Helper() + // Get gRPC address from environment or use default address := os.Getenv("CATALOG_GRPC_ADDRESS") if address == "" { @@ -614,8 +648,8 @@ func setupTestStore(t *testing.T) *catalogAddressRefStore { // Create store store := newCatalogAddressRefStore(catalogAddressRefStoreConfig{ - Domain: "test-domain", - Environment: "catalog_testing", + Domain: domain, + Environment: environment, Client: catalogClient, }) diff --git a/datastore/catalog/remote/chain_metadata_store_test.go b/datastore/catalog/remote/chain_metadata_store_test.go index 42fb2c73c..995814167 100644 --- a/datastore/catalog/remote/chain_metadata_store_test.go +++ b/datastore/catalog/remote/chain_metadata_store_test.go @@ -40,8 +40,9 @@ func newTestChainMetadata(name string) TestChainMetadata { } // setupTestChainStore creates a real gRPC client connection to a local service -func setupTestChainStore(t *testing.T) *catalogChainMetadataStore { +func setupTestChainStore(t *testing.T, domain, environment string) *catalogChainMetadataStore { t.Helper() + // Get gRPC address from environment or use default address := os.Getenv("CATALOG_GRPC_ADDRESS") if address == "" { @@ -70,8 +71,8 @@ func setupTestChainStore(t *testing.T) *catalogChainMetadataStore { // Create store store := newCatalogChainMetadataStore(catalogChainMetadataStoreConfig{ - Domain: "test-domain", - Environment: "catalog_testing", + Domain: domain, + Environment: environment, Client: catalogClient, }) @@ -133,7 +134,7 @@ func TestCatalogChainMetadataStore_Get(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestChainStore(t) + store := setupTestChainStore(t, "test-domain", "catalog_testing") key := tt.setup(store) @@ -188,7 +189,7 @@ func TestCatalogChainMetadataStore_Add(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestChainStore(t) + store := setupTestChainStore(t, "test-domain", "catalog_testing") metadata := tt.setup(store) @@ -277,7 +278,7 @@ func TestCatalogChainMetadataStore_Update(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestChainStore(t) + store := setupTestChainStore(t, "test-domain", "catalog_testing") metadata := tt.setup(store) @@ -370,7 +371,7 @@ func TestCatalogChainMetadataStore_Update_WithCustomUpdater(t *testing.T) { t.Parallel() // Create a fresh store and data for each test case - store := setupTestChainStore(t) + store := setupTestChainStore(t, "test-domain", "catalog_testing") // Create and add initial chain metadata original := newRandomChainMetadata() @@ -470,7 +471,7 @@ func TestCatalogChainMetadataStore_Upsert_WithCustomUpdater(t *testing.T) { t.Parallel() // Create a fresh store for each test case - store := setupTestChainStore(t) + store := setupTestChainStore(t, "test-domain", "catalog_testing") var original *datastore.ChainMetadata var key datastore.ChainMetadataKey @@ -525,9 +526,9 @@ func TestCatalogChainMetadataStore_Upsert_WithCustomUpdater(t *testing.T) { func TestCatalogChainMetadataStore_Update_StaleVersion(t *testing.T) { t.Parallel() // Create two separate stores to simulate concurrent access - store1 := setupTestChainStore(t) + store1 := setupTestChainStore(t, "", "") - store2 := setupTestChainStore(t) + store2 := setupTestChainStore(t, "", "") // Add a chain metadata record using store1 original := newRandomChainMetadata() @@ -637,7 +638,7 @@ func TestCatalogChainMetadataStore_Upsert(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestChainStore(t) + store := setupTestChainStore(t, "test-domain", "catalog_testing") metadata := tt.setup(store) @@ -663,9 +664,9 @@ func TestCatalogChainMetadataStore_Upsert(t *testing.T) { func TestCatalogChainMetadataStore_Upsert_StaleVersion(t *testing.T) { t.Parallel() // Create two separate stores to simulate concurrent access - store1 := setupTestChainStore(t) + store1 := setupTestChainStore(t, "", "") - store2 := setupTestChainStore(t) + store2 := setupTestChainStore(t, "", "") // Add a chain metadata record using store1 original := newRandomChainMetadata() @@ -713,7 +714,7 @@ func TestCatalogChainMetadataStore_Upsert_StaleVersion(t *testing.T) { func TestCatalogChainMetadataStore_Delete(t *testing.T) { t.Parallel() - store := setupTestChainStore(t) + store := setupTestChainStore(t, "", "") key := datastore.NewChainMetadataKey(12345) @@ -733,11 +734,17 @@ func TestCatalogChainMetadataStore_FetchAndFilter(t *testing.T) { setup func(store *catalogChainMetadataStore) (datastore.ChainMetadata, datastore.ChainMetadata) createFilter func(metadata1, metadata2 datastore.ChainMetadata) datastore.FilterFunc[datastore.ChainMetadataKey, datastore.ChainMetadata] minExpected int + expectError bool + errorType error + domain string + environment string verify func(t *testing.T, results []datastore.ChainMetadata, metadata1, metadata2 datastore.ChainMetadata) }{ { - name: "fetch_all", - operation: "fetch", + name: "fetch_all", + operation: "fetch", + domain: "test-domain", + environment: "catalog_testing", setup: func(store *catalogChainMetadataStore) (datastore.ChainMetadata, datastore.ChainMetadata) { // Setup test data with unique chain selectors metadata1 := newRandomChainMetadata() @@ -778,8 +785,10 @@ func TestCatalogChainMetadataStore_FetchAndFilter(t *testing.T) { }, }, { - name: "filter_by_chain_selector", - operation: "filter", + name: "filter_by_chain_selector", + operation: "filter", + domain: "test-domain", + environment: "catalog_testing", setup: func(store *catalogChainMetadataStore) (datastore.ChainMetadata, datastore.ChainMetadata) { // Setup test data with unique chain selectors metadata1 := newRandomChainMetadata() @@ -813,14 +822,27 @@ func TestCatalogChainMetadataStore_FetchAndFilter(t *testing.T) { } }, }, + { + name: "fetch_not_found", + operation: "fetch", + setup: func(store *catalogChainMetadataStore) (datastore.ChainMetadata, datastore.ChainMetadata) { + return datastore.ChainMetadata{}, datastore.ChainMetadata{} + }, + createFilter: nil, + minExpected: 0, + expectError: true, + errorType: datastore.ErrChainMetadataNotFound, + domain: "empty-domain-for-testing", + environment: "empty-env-for-testing", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - // Create a fresh store for each test case to avoid concurrency issues - store := setupTestChainStore(t) + // Create a fresh store for each test case to avoid concurrency issues + store := setupTestChainStore(t, tt.domain, tt.environment) metadata1, metadata2 := tt.setup(store) var results []datastore.ChainMetadata @@ -839,10 +861,18 @@ func TestCatalogChainMetadataStore_FetchAndFilter(t *testing.T) { } // Verify - require.NoError(t, err) - require.GreaterOrEqual(t, len(results), tt.minExpected) - if tt.verify != nil { - tt.verify(t, results, metadata1, metadata2) + if tt.expectError { + require.Error(t, err) + if tt.errorType != nil { + require.ErrorIs(t, err, tt.errorType) + } + require.Nil(t, results) + } else { + require.NoError(t, err) + require.GreaterOrEqual(t, len(results), tt.minExpected) + if tt.verify != nil { + tt.verify(t, results, metadata1, metadata2) + } } }) } @@ -1047,7 +1077,7 @@ func TestCatalogChainMetadataStore_ConversionHelpers(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestChainStore(t) + store := setupTestChainStore(t, "test-domain", "catalog_testing") tt.test(t, store) }) diff --git a/datastore/catalog/remote/contract_metadata_store_test.go b/datastore/catalog/remote/contract_metadata_store_test.go index 13196714f..568898336 100644 --- a/datastore/catalog/remote/contract_metadata_store_test.go +++ b/datastore/catalog/remote/contract_metadata_store_test.go @@ -40,8 +40,9 @@ func newTestContractMetadata(name string) TestContractMetadata { } // setupTestContractStore creates a real gRPC client connection to a local service -func setupTestContractStore(t *testing.T) *catalogContractMetadataStore { +func setupTestContractStore(t *testing.T, domain, environment string) *catalogContractMetadataStore { t.Helper() + // Get gRPC address from environment or use default address := os.Getenv("CATALOG_GRPC_ADDRESS") if address == "" { @@ -70,8 +71,8 @@ func setupTestContractStore(t *testing.T) *catalogContractMetadataStore { // Create store store := newCatalogContractMetadataStore(catalogContractMetadataStoreConfig{ - Domain: "test-domain", - Environment: "catalog_testing", + Domain: domain, + Environment: environment, Client: catalogClient, }) @@ -144,7 +145,7 @@ func TestCatalogContractMetadataStore_Get(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestContractStore(t) + store := setupTestContractStore(t, "test-domain", "catalog_testing") key := tt.setup(store) @@ -203,7 +204,7 @@ func TestCatalogContractMetadataStore_Add(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestContractStore(t) + store := setupTestContractStore(t, "test-domain", "catalog_testing") metadata := tt.setup(store) @@ -295,7 +296,7 @@ func TestCatalogContractMetadataStore_Update(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestContractStore(t) + store := setupTestContractStore(t, "test-domain", "catalog_testing") metadata := tt.setup(store) @@ -321,9 +322,9 @@ func TestCatalogContractMetadataStore_Update(t *testing.T) { func TestCatalogContractMetadataStore_Update_StaleVersion(t *testing.T) { t.Parallel() // Create two separate stores to simulate concurrent access - store1 := setupTestContractStore(t) + store1 := setupTestContractStore(t, "", "") - store2 := setupTestContractStore(t) + store2 := setupTestContractStore(t, "", "") // Add a contract metadata record using store1 original := newRandomContractMetadata() @@ -428,7 +429,7 @@ func TestCatalogContractMetadataStore_Upsert(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestContractStore(t) + store := setupTestContractStore(t, "test-domain", "catalog_testing") metadata := tt.setup(store) @@ -454,9 +455,9 @@ func TestCatalogContractMetadataStore_Upsert(t *testing.T) { func TestCatalogContractMetadataStore_Upsert_StaleVersion(t *testing.T) { t.Parallel() // Create two separate stores to simulate concurrent access - store1 := setupTestContractStore(t) + store1 := setupTestContractStore(t, "", "") - store2 := setupTestContractStore(t) + store2 := setupTestContractStore(t, "", "") // Add a contract metadata record using store1 original := newRandomContractMetadata() @@ -499,7 +500,7 @@ func TestCatalogContractMetadataStore_Upsert_StaleVersion(t *testing.T) { func TestCatalogContractMetadataStore_Delete(t *testing.T) { t.Parallel() - store := setupTestContractStore(t) + store := setupTestContractStore(t, "", "") key := datastore.NewContractMetadataKey(12345, "0x1234567890abcdef1234567890abcdef12345678") @@ -519,11 +520,17 @@ func TestCatalogContractMetadataStore_FetchAndFilter(t *testing.T) { setup func(store *catalogContractMetadataStore) (datastore.ContractMetadata, datastore.ContractMetadata) createFilter func(metadata1, metadata2 datastore.ContractMetadata) datastore.FilterFunc[datastore.ContractMetadataKey, datastore.ContractMetadata] minExpected int + expectError bool + errorType error + domain string + environment string verify func(t *testing.T, results []datastore.ContractMetadata, metadata1, metadata2 datastore.ContractMetadata) }{ { - name: "fetch_all", - operation: "fetch", + name: "fetch_all", + operation: "fetch", + domain: "test-domain", + environment: "catalog_testing", setup: func(store *catalogContractMetadataStore) (datastore.ContractMetadata, datastore.ContractMetadata) { // Setup test data with unique chain selectors metadata1 := newRandomContractMetadata() @@ -564,8 +571,10 @@ func TestCatalogContractMetadataStore_FetchAndFilter(t *testing.T) { }, }, { - name: "filter_by_chain_selector", - operation: "filter", + name: "filter_by_chain_selector", + operation: "filter", + domain: "test-domain", + environment: "catalog_testing", setup: func(store *catalogContractMetadataStore) (datastore.ContractMetadata, datastore.ContractMetadata) { // Setup test data with unique chain selectors metadata1 := newRandomContractMetadata() @@ -599,14 +608,27 @@ func TestCatalogContractMetadataStore_FetchAndFilter(t *testing.T) { } }, }, + { + name: "fetch_not_found", + operation: "fetch", + setup: func(store *catalogContractMetadataStore) (datastore.ContractMetadata, datastore.ContractMetadata) { + return datastore.ContractMetadata{}, datastore.ContractMetadata{} + }, + createFilter: nil, + minExpected: 0, + expectError: true, + errorType: datastore.ErrContractMetadataNotFound, + domain: "empty-domain-for-testing", + environment: "empty-env-for-testing", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - // Create a fresh store for each test case to avoid concurrency issues - store := setupTestContractStore(t) + // Create a fresh store for each test case to avoid concurrency issues + store := setupTestContractStore(t, tt.domain, tt.environment) metadata1, metadata2 := tt.setup(store) var results []datastore.ContractMetadata @@ -625,10 +647,18 @@ func TestCatalogContractMetadataStore_FetchAndFilter(t *testing.T) { } // Verify - require.NoError(t, err) - require.GreaterOrEqual(t, len(results), tt.minExpected) - if tt.verify != nil { - tt.verify(t, results, metadata1, metadata2) + if tt.expectError { + require.Error(t, err) + if tt.errorType != nil { + require.ErrorIs(t, err, tt.errorType) + } + require.Nil(t, results) + } else { + require.NoError(t, err) + require.GreaterOrEqual(t, len(results), tt.minExpected) + if tt.verify != nil { + tt.verify(t, results, metadata1, metadata2) + } } }) } @@ -752,7 +782,7 @@ func TestCatalogContractMetadataStore_ConversionHelpers(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestContractStore(t) + store := setupTestContractStore(t, "test-domain", "catalog_testing") tt.test(t, store) }) @@ -1040,7 +1070,7 @@ func TestCatalogContractMetadataStore_Update_WithCustomUpdater(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - store := setupTestContractStore(t) + store := setupTestContractStore(t, "test-domain", "catalog_testing") key := tt.setup(store) @@ -1168,7 +1198,7 @@ func TestCatalogContractMetadataStore_Upsert_WithCustomUpdater(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - store := setupTestContractStore(t) + store := setupTestContractStore(t, "test-domain", "catalog_testing") key := tt.setup(store) diff --git a/datastore/catalog/remote/env_metadata_store_test.go b/datastore/catalog/remote/env_metadata_store_test.go index 02808da4f..fbf8286dc 100644 --- a/datastore/catalog/remote/env_metadata_store_test.go +++ b/datastore/catalog/remote/env_metadata_store_test.go @@ -29,9 +29,16 @@ type TestEnvMetadata struct { Tags []string `json:"tags,omitempty"` } +// generateRandomDomain generates a random domain name for test isolation +func generateRandomDomain() string { + random, _ := rand.Int(rand.Reader, big.NewInt(1000000000)) + return fmt.Sprintf("test-domain-%d", random) +} + // setupTestEnvStore creates a test environment metadata store and gRPC connection -func setupTestEnvStore(t *testing.T, overrideDomain string) *catalogEnvMetadataStore { +func setupTestEnvStore(t *testing.T, domain, environment string) *catalogEnvMetadataStore { t.Helper() + address := os.Getenv("CATALOG_GRPC_ADDRESS") if address == "" { address = defaultEnvGRPCAddress @@ -54,19 +61,9 @@ func setupTestEnvStore(t *testing.T, overrideDomain string) *catalogEnvMetadataS return nil } - // Create store with a unique domain name per test to ensure isolation - random, err := rand.Int(rand.Reader, big.NewInt(1000000000)) - require.NoError(t, err) - var domain string - if overrideDomain != "" { - domain = overrideDomain - } else { - domain = fmt.Sprintf("test-domain-%d", random) - } - store := newCatalogEnvMetadataStore(catalogEnvMetadataStoreConfig{ Domain: domain, - Environment: "catalog_testing", // Use static environment name + Environment: environment, Client: catalogClient, }) t.Cleanup(func() { @@ -118,6 +115,8 @@ func TestCatalogEnvMetadataStore_Get(t *testing.T) { setupRecords []datastore.EnvMetadata wantRecord datastore.EnvMetadata wantErr error + domain string + environment string }{ { name: "success", @@ -135,15 +134,26 @@ func TestCatalogEnvMetadataStore_Get(t *testing.T) { Version: "1.0.0", }, }, - wantErr: nil, + wantErr: nil, + domain: generateRandomDomain(), + environment: "catalog_testing", + }, + { + name: "not_found", + setupRecords: []datastore.EnvMetadata{}, + wantRecord: datastore.EnvMetadata{}, + wantErr: datastore.ErrEnvMetadataNotSet, + domain: "empty-domain-for-testing", + environment: "empty-env-for-testing", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() + // Create store for testing - store := setupTestEnvStore(t, "") + store := setupTestEnvStore(t, tt.domain, tt.environment) // Setup test data if needed for _, record := range tt.setupRecords { @@ -156,14 +166,8 @@ func TestCatalogEnvMetadataStore_Get(t *testing.T) { // Verify error if tt.wantErr != nil { - if err == nil { - t.Errorf("Expected error %v, got nil", tt.wantErr) - return - } - if err.Error() != tt.wantErr.Error() { - t.Errorf("Expected error %v, got %v", tt.wantErr, err) - return - } + require.Error(t, err) + require.ErrorIs(t, err, tt.wantErr) return } @@ -198,7 +202,7 @@ func TestCatalogEnvMetadataStore_Set(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() - store := setupTestEnvStore(t, "") + store := setupTestEnvStore(t, generateRandomDomain(), "catalog_testing") // Test Set operation err := store.Set(t.Context(), tt.record.Metadata) @@ -230,7 +234,7 @@ func TestCatalogEnvMetadataStore_Set(t *testing.T) { func TestCatalogEnvMetadataStore_Set_Update(t *testing.T) { t.Parallel() - store := setupTestEnvStore(t, "") + store := setupTestEnvStore(t, "", "") // Set initial record initialMetadata := TestEnvMetadata{ @@ -285,8 +289,8 @@ func TestCatalogEnvMetadataStore_Set_ConcurrentUpdates(t *testing.T) { random, err := rand.Int(rand.Reader, big.NewInt(1000000000)) require.NoError(t, err) domain := fmt.Sprintf("test-domain-%d", random) - store1 := setupTestEnvStore(t, domain) - store2 := setupTestEnvStore(t, domain) + store1 := setupTestEnvStore(t, domain, "") + store2 := setupTestEnvStore(t, domain, "") // Set initial record with store1 initialMetadata := TestEnvMetadata{ @@ -429,7 +433,7 @@ func TestCatalogEnvMetadataStore_ConversionHelpers(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() // Create a fresh store for each test case to avoid concurrency issues - store := setupTestEnvStore(t, "") + store := setupTestEnvStore(t, generateRandomDomain(), "catalog_testing") tt.test(t, store) }) From 7fa08ad2ec5938b95ecc124666465cbb4b79ad80 Mon Sep 17 00:00:00 2001 From: giogam <151543+giogam@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:52:50 +0100 Subject: [PATCH 4/5] chore(catalog): removes non needed change --- datastore/catalog/remote/env_metadata_store.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/datastore/catalog/remote/env_metadata_store.go b/datastore/catalog/remote/env_metadata_store.go index 05c7d553e..3550b523d 100644 --- a/datastore/catalog/remote/env_metadata_store.go +++ b/datastore/catalog/remote/env_metadata_store.go @@ -8,7 +8,6 @@ import ( "sync" "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" @@ -101,6 +100,7 @@ func (s *catalogEnvMetadataStore) envMetadataToProto(record datastore.EnvMetadat RowVersion: version, } } + func (s *catalogEnvMetadataStore) Get( _ context.Context, options ...datastore.GetOption, @@ -115,6 +115,7 @@ func (s *catalogEnvMetadataStore) Get( return s.get(ignoreTransactions) } + func (s *catalogEnvMetadataStore) get(ignoreTransaction bool) (datastore.EnvMetadata, error) { stream, err := s.client.DataAccess() if err != nil { @@ -132,11 +133,6 @@ func (s *catalogEnvMetadataStore) get(ignoreTransaction bool) (datastore.EnvMeta } if sendErr := stream.Send(findReq); sendErr != nil { - st, _ := status.FromError(sendErr) - if st.Code() == codes.NotFound { - return datastore.EnvMetadata{}, datastore.ErrEnvMetadataNotSet - } - return datastore.EnvMetadata{}, fmt.Errorf("failed to send find request: %w", sendErr) } From edc3dc35d61a6b9f18ef13cd32c9befb54627c70 Mon Sep 17 00:00:00 2001 From: giogam <151543+giogam@users.noreply.github.com> Date: Thu, 30 Oct 2025 18:06:18 +0100 Subject: [PATCH 5/5] chore(catalog): refactor error checking --- datastore/catalog/remote/address_ref_store.go | 11 +++++++---- datastore/catalog/remote/chain_metadata_store.go | 13 ++++++++----- datastore/catalog/remote/contract_metadata_store.go | 9 ++++++--- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/datastore/catalog/remote/address_ref_store.go b/datastore/catalog/remote/address_ref_store.go index dac28e092..b41e1d0a6 100644 --- a/datastore/catalog/remote/address_ref_store.go +++ b/datastore/catalog/remote/address_ref_store.go @@ -7,7 +7,6 @@ import ( "github.com/Masterminds/semver/v3" "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" @@ -157,13 +156,17 @@ func (s *catalogAddressRefStore) Fetch(_ context.Context) ([]datastore.AddressRe } // Check for errors in the response - if err := parseResponseStatus(response.Status); err != nil { - st, _ := status.FromError(err) + if statusErr := parseResponseStatus(response.Status); statusErr != nil { + st, sterr := parseStatusError(statusErr) + if sterr != nil { + return nil, sterr + } + if st.Code() == codes.NotFound { return nil, datastore.ErrAddressRefNotFound } - return nil, fmt.Errorf("fetch address refs failed: %w", err) + return nil, fmt.Errorf("fetch address refs failed: %w", statusErr) } // Extract the address find response diff --git a/datastore/catalog/remote/chain_metadata_store.go b/datastore/catalog/remote/chain_metadata_store.go index 355741fb0..f4ac99fe1 100644 --- a/datastore/catalog/remote/chain_metadata_store.go +++ b/datastore/catalog/remote/chain_metadata_store.go @@ -8,7 +8,6 @@ import ( "sync" "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" @@ -210,13 +209,17 @@ func (s *catalogChainMetadataStore) Fetch(_ context.Context) ([]datastore.ChainM } // Check for errors in the response - if err := parseResponseStatus(resp.Status); err != nil { - st, _ := status.FromError(err) + if statusErr := parseResponseStatus(resp.Status); statusErr != nil { + st, sterr := parseStatusError(statusErr) + if sterr != nil { + return nil, sterr + } + if st.Code() == codes.NotFound { - return nil, datastore.ErrChainMetadataNotFound + return nil, fmt.Errorf("%w: %s", datastore.ErrChainMetadataNotFound, statusErr.Error()) } - return nil, fmt.Errorf("fetch chain metadata failed: %w", err) + return nil, fmt.Errorf("fetch chain metadata failed: %w", statusErr) } findResp := resp.GetChainMetadataFindResponse() diff --git a/datastore/catalog/remote/contract_metadata_store.go b/datastore/catalog/remote/contract_metadata_store.go index 4367549d6..40974bf9c 100644 --- a/datastore/catalog/remote/contract_metadata_store.go +++ b/datastore/catalog/remote/contract_metadata_store.go @@ -9,7 +9,6 @@ import ( "sync" "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/wrapperspb" "github.com/smartcontractkit/chainlink-deployments-framework/datastore" @@ -215,9 +214,13 @@ func (s *catalogContractMetadataStore) Fetch(_ context.Context) ([]datastore.Con // Check for errors in the response if statusErr := parseResponseStatus(resp.Status); statusErr != nil { - st, _ := status.FromError(statusErr) + st, sterr := parseStatusError(statusErr) + if sterr != nil { + return nil, sterr + } + if st.Code() == codes.NotFound { - return nil, datastore.ErrContractMetadataNotFound + return nil, fmt.Errorf("%w: %s", datastore.ErrContractMetadataNotFound, statusErr.Error()) } return nil, fmt.Errorf("fetch contract metadata failed: %w", statusErr)