From 3362b74885f96cb9494799741639debcb2aad44e Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Wed, 8 Apr 2026 13:17:20 +0530 Subject: [PATCH 1/4] feat: indexer-backed token provider --- pkg/cantonsdk/token/client.go | 7 +- pkg/indexer/client/http.go | 206 ++++++++++++++ pkg/indexer/client/http_test.go | 401 +++++++++++++++++++++++++++ pkg/token/erc20_test.go | 10 +- pkg/token/mocks/mock_canton_token.go | 58 ++++ pkg/token/mocks/mock_provider.go | 20 +- pkg/token/provider/canton.go | 6 +- pkg/token/provider/indexer.go | 71 +++++ pkg/token/service.go | 4 +- 9 files changed, 761 insertions(+), 22 deletions(-) create mode 100644 pkg/indexer/client/http.go create mode 100644 pkg/indexer/client/http_test.go create mode 100644 pkg/token/provider/indexer.go diff --git a/pkg/cantonsdk/token/client.go b/pkg/cantonsdk/token/client.go index 491755c..8adf9d8 100644 --- a/pkg/cantonsdk/token/client.go +++ b/pkg/cantonsdk/token/client.go @@ -60,6 +60,9 @@ type Token interface { // GetBalanceByFingerprint returns the owner's total balance (sum of holdings) for the token symbol. GetBalanceByFingerprint(ctx context.Context, fingerprint string, tokenSymbol string) (string, error) + // GetBalanceByPartyID returns the owner's total balance (sum of holdings) for the token symbol. + GetBalanceByPartyID(ctx context.Context, partyID string, tokenSymbol string) (string, error) + // GetTotalSupply returns the total supply (sum across all holdings) for the token symbol. GetTotalSupply(ctx context.Context, tokenSymbol string) (string, error) @@ -305,10 +308,10 @@ func (c *Client) GetBalanceByFingerprint(ctx context.Context, fingerprint string if err != nil { return "0", err } - return c.getBalanceByPartyID(ctx, m.UserParty, tokenSymbol) + return c.GetBalanceByPartyID(ctx, m.UserParty, tokenSymbol) } -func (c *Client) getBalanceByPartyID(ctx context.Context, partyID string, tokenSymbol string) (string, error) { +func (c *Client) GetBalanceByPartyID(ctx context.Context, partyID string, tokenSymbol string) (string, error) { holdings, err := c.GetHoldings(ctx, partyID, tokenSymbol) if err != nil { return "0", err diff --git a/pkg/indexer/client/http.go b/pkg/indexer/client/http.go new file mode 100644 index 0000000..b812ede --- /dev/null +++ b/pkg/indexer/client/http.go @@ -0,0 +1,206 @@ +// Package client provides an HTTP client for the indexer's admin API. +package client + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "strconv" + + apperrors "github.com/chainsafe/canton-middleware/pkg/app/errors" + "github.com/chainsafe/canton-middleware/pkg/indexer" +) + +// Client is the read interface over the indexer's HTTP admin API. +// Its method set mirrors indexer/service.Service so that callers are agnostic +// to whether they are talking to an in-process service or a remote indexer. +// +//go:generate mockery --name Client --output mocks --outpkg mocks --filename mock_client.go --with-expecter +type Client interface { + // Token queries + GetToken(ctx context.Context, admin, id string) (*indexer.Token, error) + ListTokens(ctx context.Context, p indexer.Pagination) (*indexer.Page[*indexer.Token], error) + + // ERC-20 analogs + TotalSupply(ctx context.Context, admin, id string) (string, error) + + // Balance queries + GetBalance(ctx context.Context, partyID, admin, id string) (*indexer.Balance, error) + ListBalancesForParty(ctx context.Context, partyID string, p indexer.Pagination) (*indexer.Page[*indexer.Balance], error) + ListBalancesForToken(ctx context.Context, admin, id string, p indexer.Pagination) (*indexer.Page[*indexer.Balance], error) + + // Audit trail + GetEvent(ctx context.Context, contractID string) (*indexer.ParsedEvent, error) + ListTokenEvents(ctx context.Context, admin, id string, f indexer.EventFilter, p indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error) + ListPartyEvents(ctx context.Context, partyID string, f indexer.EventFilter, p indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error) +} + +// HTTP implements Client by calling the indexer's unauthenticated admin HTTP API. +// All paths are under /indexer/v1/admin. +type HTTP struct { + baseURL string + httpClient *http.Client +} + +// New creates an HTTP-backed indexer client. +// baseURL is the indexer's base URL without a trailing slash (e.g. "http://localhost:8080"). +// httpClient may be nil; http.DefaultClient is used in that case. +func New(baseURL string, httpClient *http.Client) *HTTP { + if httpClient == nil { + httpClient = http.DefaultClient + } + return &HTTP{baseURL: baseURL, httpClient: httpClient} +} + +// GetToken calls GET /indexer/v1/admin/tokens/{admin}/{id}. +func (c *HTTP) GetToken(ctx context.Context, admin, id string) (*indexer.Token, error) { + u := c.tokenBase(admin, id) + var t indexer.Token + if err := c.getJSON(ctx, u, &t); err != nil { + return nil, fmt.Errorf("get token %s/%s: %w", admin, id, err) + } + return &t, nil +} + +// ListTokens calls GET /indexer/v1/admin/tokens. +func (c *HTTP) ListTokens(ctx context.Context, p indexer.Pagination) (*indexer.Page[*indexer.Token], error) { + u := c.baseURL + "/indexer/v1/admin/tokens?" + pageQuery(p).Encode() + var page indexer.Page[*indexer.Token] + if err := c.getJSON(ctx, u, &page); err != nil { + return nil, fmt.Errorf("list tokens: %w", err) + } + return &page, nil +} + +// TotalSupply calls GET /indexer/v1/admin/tokens/{admin}/{id}/supply. +func (c *HTTP) TotalSupply(ctx context.Context, admin, id string) (string, error) { + u := c.tokenBase(admin, id) + "/supply" + var resp struct { + TotalSupply string `json:"total_supply"` + } + if err := c.getJSON(ctx, u, &resp); err != nil { + return "0", fmt.Errorf("total supply for %s/%s: %w", admin, id, err) + } + return resp.TotalSupply, nil +} + +// GetBalance calls GET /indexer/v1/admin/parties/{partyID}/balances/{admin}/{id}. +// Returns apperrors.ResourceNotFoundError when the party has no balance record. +func (c *HTTP) GetBalance(ctx context.Context, partyID, admin, id string) (*indexer.Balance, error) { + u := c.partyBase(partyID) + "/balances/" + url.PathEscape(admin) + "/" + url.PathEscape(id) + var b indexer.Balance + if err := c.getJSON(ctx, u, &b); err != nil { + return nil, fmt.Errorf("balance for party %s token %s/%s: %w", partyID, admin, id, err) + } + return &b, nil +} + +// ListBalancesForParty calls GET /indexer/v1/admin/parties/{partyID}/balances. +func (c *HTTP) ListBalancesForParty(ctx context.Context, partyID string, p indexer.Pagination) (*indexer.Page[*indexer.Balance], error) { + u := c.partyBase(partyID) + "/balances?" + pageQuery(p).Encode() + var page indexer.Page[*indexer.Balance] + if err := c.getJSON(ctx, u, &page); err != nil { + return nil, fmt.Errorf("list balances for party %s: %w", partyID, err) + } + return &page, nil +} + +// ListBalancesForToken calls GET /indexer/v1/admin/tokens/{admin}/{id}/balances. +func (c *HTTP) ListBalancesForToken(ctx context.Context, admin, id string, p indexer.Pagination) (*indexer.Page[*indexer.Balance], error) { + u := c.tokenBase(admin, id) + "/balances?" + pageQuery(p).Encode() + var page indexer.Page[*indexer.Balance] + if err := c.getJSON(ctx, u, &page); err != nil { + return nil, fmt.Errorf("list balances for token %s/%s: %w", admin, id, err) + } + return &page, nil +} + +// GetEvent calls GET /indexer/v1/admin/events/{contractID}. +func (c *HTTP) GetEvent(ctx context.Context, contractID string) (*indexer.ParsedEvent, error) { + u := c.baseURL + "/indexer/v1/admin/events/" + url.PathEscape(contractID) + var e indexer.ParsedEvent + if err := c.getJSON(ctx, u, &e); err != nil { + return nil, fmt.Errorf("get event %s: %w", contractID, err) + } + return &e, nil +} + +// ListTokenEvents calls GET /indexer/v1/admin/tokens/{admin}/{id}/events. +func (c *HTTP) ListTokenEvents(ctx context.Context, admin, id string, f indexer.EventFilter, p indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error) { + q := pageQuery(p) + if f.EventType != "" { + q.Set("event_type", string(f.EventType)) + } + u := c.tokenBase(admin, id) + "/events?" + q.Encode() + var page indexer.Page[*indexer.ParsedEvent] + if err := c.getJSON(ctx, u, &page); err != nil { + return nil, fmt.Errorf("list events for token %s/%s: %w", admin, id, err) + } + return &page, nil +} + +// ListPartyEvents calls GET /indexer/v1/admin/parties/{partyID}/events. +func (c *HTTP) ListPartyEvents(ctx context.Context, partyID string, f indexer.EventFilter, p indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error) { + q := pageQuery(p) + if f.EventType != "" { + q.Set("event_type", string(f.EventType)) + } + u := c.partyBase(partyID) + "/events?" + q.Encode() + var page indexer.Page[*indexer.ParsedEvent] + if err := c.getJSON(ctx, u, &page); err != nil { + return nil, fmt.Errorf("list events for party %s: %w", partyID, err) + } + return &page, nil +} + +func (c *HTTP) tokenBase(admin, id string) string { + return fmt.Sprintf("%s/indexer/v1/admin/tokens/%s/%s", + c.baseURL, url.PathEscape(admin), url.PathEscape(id)) +} + +func (c *HTTP) partyBase(partyID string) string { + return fmt.Sprintf("%s/indexer/v1/admin/parties/%s", + c.baseURL, url.PathEscape(partyID)) +} + +func pageQuery(p indexer.Pagination) url.Values { + q := url.Values{} + q.Set("page", strconv.Itoa(p.Page)) + q.Set("limit", strconv.Itoa(p.Limit)) + return q +} + +// getJSON performs a GET request and JSON-decodes a successful response into dest. +// Non-2xx responses are translated to typed app errors: +// - 404 → apperrors.ResourceNotFoundError +// - other → plain fmt.Errorf with status and body message +func (c *HTTP) getJSON(ctx context.Context, rawURL string, dest any) error { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, rawURL, nil) + if err != nil { + return fmt.Errorf("build request: %w", err) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return fmt.Errorf("http request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + var body struct { + Error string `json:"error"` + } + _ = json.NewDecoder(resp.Body).Decode(&body) + if resp.StatusCode == http.StatusNotFound { + return apperrors.ResourceNotFoundError(nil, body.Error) + } + return fmt.Errorf("indexer HTTP %d: %s", resp.StatusCode, body.Error) + } + + if err := json.NewDecoder(resp.Body).Decode(dest); err != nil { + return fmt.Errorf("decode response: %w", err) + } + return nil +} diff --git a/pkg/indexer/client/http_test.go b/pkg/indexer/client/http_test.go new file mode 100644 index 0000000..1845080 --- /dev/null +++ b/pkg/indexer/client/http_test.go @@ -0,0 +1,401 @@ +package client_test + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + apperrors "github.com/chainsafe/canton-middleware/pkg/app/errors" + "github.com/chainsafe/canton-middleware/pkg/indexer" + "github.com/chainsafe/canton-middleware/pkg/indexer/client" +) + +// ── fixtures ───────────────────────────────────────────────────────────────── + +const ( + admin = "admin::demo123@domain" + id = "DEMO" + partyID = "alice::abc123@domain" + contractID = "contract::abc@node" +) + +var ( + testPagination = indexer.Pagination{Page: 1, Limit: 50} + testToken = &indexer.Token{ + InstrumentAdmin: admin, + InstrumentID: id, + Issuer: "issuer::xyz@domain", + TotalSupply: "1000.000000000000000000", + HolderCount: 3, + FirstSeenOffset: 42, + FirstSeenAt: time.Unix(0, 0).UTC(), + } + testBalance = &indexer.Balance{ + PartyID: partyID, + InstrumentAdmin: admin, + InstrumentID: id, + Amount: "500.000000000000000000", + } + testEvent = &indexer.ParsedEvent{ + InstrumentAdmin: admin, + InstrumentID: id, + Issuer: "issuer::xyz@domain", + EventType: indexer.EventMint, + Amount: "100.000000000000000000", + ContractID: contractID, + TxID: "tx123", + LedgerOffset: 10, + Timestamp: time.Unix(0, 0).UTC(), + EffectiveTime: time.Unix(0, 0).UTC(), + } +) + +// ── helpers ─────────────────────────────────────────────────────────────────── + +func jsonResp(status int, body any) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + _ = json.NewEncoder(w).Encode(body) + } +} + +func errResp(status int, message string) http.HandlerFunc { + return jsonResp(status, map[string]any{"error": message, "code": status}) +} + +func pageOf[T any](items []T) indexer.Page[T] { + return indexer.Page[T]{Items: items, Total: int64(len(items)), Page: 1, Limit: 50} +} + +// assertPagination checks that page and limit query params are present. +func assertPagination(t *testing.T, r *http.Request) { + t.Helper() + assert.Equal(t, "1", r.URL.Query().Get("page")) + assert.Equal(t, "50", r.URL.Query().Get("limit")) +} + +// ── GetToken ───────────────────────────────────────────────────────────────── + +func TestHTTP_GetToken_Success(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, fmt.Sprintf("/indexer/v1/admin/tokens/%s/%s", admin, id), r.URL.Path) + jsonResp(http.StatusOK, testToken)(w, r) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.GetToken(context.Background(), admin, id) + + require.NoError(t, err) + assert.Equal(t, testToken.InstrumentAdmin, got.InstrumentAdmin) + assert.Equal(t, testToken.TotalSupply, got.TotalSupply) +} + +func TestHTTP_GetToken_NotFound(t *testing.T) { + srv := httptest.NewServer(errResp(http.StatusNotFound, "token not found")) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + _, err := c.GetToken(context.Background(), admin, id) + + require.Error(t, err) + assert.True(t, apperrors.Is(err, apperrors.CategoryResourceNotFound)) +} + +// ── ListTokens ──────────────────────────────────────────────────────────────── + +func TestHTTP_ListTokens_Success(t *testing.T) { + page := pageOf([]*indexer.Token{testToken}) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/indexer/v1/admin/tokens", r.URL.Path) + assertPagination(t, r) + jsonResp(http.StatusOK, page)(w, r) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.ListTokens(context.Background(), testPagination) + + require.NoError(t, err) + assert.Equal(t, int64(1), got.Total) + assert.Len(t, got.Items, 1) +} + +func TestHTTP_ListTokens_Empty(t *testing.T) { + page := pageOf([]*indexer.Token{}) + srv := httptest.NewServer(jsonResp(http.StatusOK, page)) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.ListTokens(context.Background(), testPagination) + + require.NoError(t, err) + assert.Empty(t, got.Items) +} + +// ── TotalSupply ─────────────────────────────────────────────────────────────── + +func TestHTTP_TotalSupply_Success(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, fmt.Sprintf("/indexer/v1/admin/tokens/%s/%s/supply", admin, id), r.URL.Path) + jsonResp(http.StatusOK, map[string]string{"total_supply": "1000.000000000000000000"})(w, r) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + supply, err := c.TotalSupply(context.Background(), admin, id) + + require.NoError(t, err) + assert.Equal(t, "1000.000000000000000000", supply) +} + +func TestHTTP_TotalSupply_ServerError(t *testing.T) { + srv := httptest.NewServer(errResp(http.StatusInternalServerError, "database unavailable")) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + supply, err := c.TotalSupply(context.Background(), admin, id) + + assert.Equal(t, "0", supply) + require.Error(t, err) + assert.ErrorContains(t, err, "indexer HTTP 500") +} + +func TestHTTP_TotalSupply_NetworkError(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + srv.Close() + + c := client.New(srv.URL, srv.Client()) + supply, err := c.TotalSupply(context.Background(), admin, id) + + assert.Equal(t, "0", supply) + require.Error(t, err) +} + +func TestHTTP_TotalSupply_MalformedResponse(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("not json")) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + supply, err := c.TotalSupply(context.Background(), admin, id) + + assert.Equal(t, "0", supply) + require.Error(t, err) + assert.ErrorContains(t, err, "decode response") +} + +// ── GetBalance ──────────────────────────────────────────────────────────────── + +func TestHTTP_GetBalance_Success(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, + fmt.Sprintf("/indexer/v1/admin/parties/%s/balances/%s/%s", partyID, admin, id), + r.URL.Path, + ) + jsonResp(http.StatusOK, testBalance)(w, r) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.GetBalance(context.Background(), partyID, admin, id) + + require.NoError(t, err) + assert.Equal(t, "500.000000000000000000", got.Amount) + assert.Equal(t, partyID, got.PartyID) +} + +func TestHTTP_GetBalance_NotFound_ReturnsResourceNotFoundError(t *testing.T) { + // 404 is surfaced as apperrors.ResourceNotFoundError, not swallowed. + // The provider layer (not the client) is responsible for converting not-found to "0". + srv := httptest.NewServer(errResp(http.StatusNotFound, "balance not found")) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.GetBalance(context.Background(), partyID, admin, id) + + assert.Nil(t, got) + require.Error(t, err) + assert.True(t, apperrors.Is(err, apperrors.CategoryResourceNotFound)) +} + +func TestHTTP_GetBalance_ServerError(t *testing.T) { + srv := httptest.NewServer(errResp(http.StatusBadGateway, "upstream failure")) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.GetBalance(context.Background(), partyID, admin, id) + + assert.Nil(t, got) + require.Error(t, err) + assert.ErrorContains(t, err, "indexer HTTP 502") +} + +func TestHTTP_GetBalance_NetworkError(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.GetBalance(context.Background(), partyID, admin, id) + + assert.Nil(t, got) + require.Error(t, err) +} + +// ── ListBalancesForParty ────────────────────────────────────────────────────── + +func TestHTTP_ListBalancesForParty_Success(t *testing.T) { + page := pageOf([]*indexer.Balance{testBalance}) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, fmt.Sprintf("/indexer/v1/admin/parties/%s/balances", partyID), r.URL.Path) + assertPagination(t, r) + jsonResp(http.StatusOK, page)(w, r) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.ListBalancesForParty(context.Background(), partyID, testPagination) + + require.NoError(t, err) + assert.Len(t, got.Items, 1) + assert.Equal(t, "500.000000000000000000", got.Items[0].Amount) +} + +// ── ListBalancesForToken ────────────────────────────────────────────────────── + +func TestHTTP_ListBalancesForToken_Success(t *testing.T) { + page := pageOf([]*indexer.Balance{testBalance}) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, + fmt.Sprintf("/indexer/v1/admin/tokens/%s/%s/balances", admin, id), + r.URL.Path, + ) + assertPagination(t, r) + jsonResp(http.StatusOK, page)(w, r) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.ListBalancesForToken(context.Background(), admin, id, testPagination) + + require.NoError(t, err) + assert.Len(t, got.Items, 1) +} + +// ── GetEvent ────────────────────────────────────────────────────────────────── + +func TestHTTP_GetEvent_Success(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, fmt.Sprintf("/indexer/v1/admin/events/%s", contractID), r.URL.Path) + jsonResp(http.StatusOK, testEvent)(w, r) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.GetEvent(context.Background(), contractID) + + require.NoError(t, err) + assert.Equal(t, contractID, got.ContractID) + assert.Equal(t, indexer.EventMint, got.EventType) +} + +func TestHTTP_GetEvent_NotFound(t *testing.T) { + srv := httptest.NewServer(errResp(http.StatusNotFound, "event not found")) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + _, err := c.GetEvent(context.Background(), contractID) + + require.Error(t, err) + assert.True(t, apperrors.Is(err, apperrors.CategoryResourceNotFound)) +} + +// ── ListTokenEvents ─────────────────────────────────────────────────────────── + +func TestHTTP_ListTokenEvents_NoFilter(t *testing.T) { + page := pageOf([]*indexer.ParsedEvent{testEvent}) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, + fmt.Sprintf("/indexer/v1/admin/tokens/%s/%s/events", admin, id), + r.URL.Path, + ) + assertPagination(t, r) + assert.Empty(t, r.URL.Query().Get("event_type"), "event_type should be absent when filter is empty") + jsonResp(http.StatusOK, page)(w, r) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.ListTokenEvents(context.Background(), admin, id, indexer.EventFilter{}, testPagination) + + require.NoError(t, err) + assert.Len(t, got.Items, 1) +} + +func TestHTTP_ListTokenEvents_WithEventTypeFilter(t *testing.T) { + page := pageOf([]*indexer.ParsedEvent{testEvent}) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, string(indexer.EventMint), r.URL.Query().Get("event_type")) + jsonResp(http.StatusOK, page)(w, r) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.ListTokenEvents(context.Background(), admin, id, + indexer.EventFilter{EventType: indexer.EventMint}, testPagination) + + require.NoError(t, err) + assert.Len(t, got.Items, 1) +} + +// ── ListPartyEvents ─────────────────────────────────────────────────────────── + +func TestHTTP_ListPartyEvents_NoFilter(t *testing.T) { + page := pageOf([]*indexer.ParsedEvent{testEvent}) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, fmt.Sprintf("/indexer/v1/admin/parties/%s/events", partyID), r.URL.Path) + assertPagination(t, r) + assert.Empty(t, r.URL.Query().Get("event_type")) + jsonResp(http.StatusOK, page)(w, r) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.ListPartyEvents(context.Background(), partyID, indexer.EventFilter{}, testPagination) + + require.NoError(t, err) + assert.Len(t, got.Items, 1) +} + +func TestHTTP_ListPartyEvents_WithEventTypeFilter(t *testing.T) { + page := pageOf([]*indexer.ParsedEvent{testEvent}) + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, string(indexer.EventBurn), r.URL.Query().Get("event_type")) + jsonResp(http.StatusOK, page)(w, r) + })) + defer srv.Close() + + c := client.New(srv.URL, srv.Client()) + got, err := c.ListPartyEvents(context.Background(), partyID, + indexer.EventFilter{EventType: indexer.EventBurn}, testPagination) + + require.NoError(t, err) + assert.Len(t, got.Items, 1) +} + +// ── NilHTTPClient uses DefaultClient ───────────────────────────────────────── + +func TestNew_NilHTTPClient_UsesDefaultClient(t *testing.T) { + c := client.New("http://localhost:8080", nil) + require.NotNil(t, c) +} diff --git a/pkg/token/erc20_test.go b/pkg/token/erc20_test.go index 426b1ae..b6bfa92 100644 --- a/pkg/token/erc20_test.go +++ b/pkg/token/erc20_test.go @@ -35,11 +35,11 @@ func newCfg() *token.Config { } func promptUser() *user.User { - return &user.User{Fingerprint: "fpA"} + return &user.User{CantonPartyID: "partyA", Fingerprint: "fpA"} } func demoUser() *user.User { - return &user.User{Fingerprint: "fpB"} + return &user.User{CantonPartyID: "partyB", Fingerprint: "fpB"} } // ─── TestERC20_Name ─────────────────────────────────────────────────────────── @@ -184,7 +184,7 @@ func TestERC20_BalanceOf(t *testing.T) { userStore.EXPECT().GetUserByEVMAddress(mock.Anything, accountAddr.Hex()).Return(promptUser(), nil) provider := mocks.NewProvider(t) - provider.EXPECT().GetBalance(mock.Anything, "PROMPT", promptUser().Fingerprint).Return("100", nil) + provider.EXPECT().GetBalance(mock.Anything, "PROMPT", promptUser().CantonPartyID).Return("100", nil) svc := token.NewTokenService(newCfg(), provider, userStore, nil) erc20 := token.NewERC20(promptAddr, svc) @@ -200,7 +200,7 @@ func TestERC20_BalanceOf(t *testing.T) { userStore.EXPECT().GetUserByEVMAddress(mock.Anything, accountAddr.Hex()).Return(demoUser(), nil) provider := mocks.NewProvider(t) - provider.EXPECT().GetBalance(mock.Anything, "DEMO", demoUser().Fingerprint).Return("50", nil) + provider.EXPECT().GetBalance(mock.Anything, "DEMO", demoUser().CantonPartyID).Return("50", nil) svc := token.NewTokenService(newCfg(), provider, userStore, nil) erc20 := token.NewERC20(demoAddr, svc) @@ -229,7 +229,7 @@ func TestERC20_BalanceOf(t *testing.T) { userStore.EXPECT().GetUserByEVMAddress(mock.Anything, accountAddr.Hex()).Return(promptUser(), nil) provider := mocks.NewProvider(t) - provider.EXPECT().GetBalance(mock.Anything, "PROMPT", promptUser().Fingerprint).Return("0", errors.New("timeout")) + provider.EXPECT().GetBalance(mock.Anything, "PROMPT", promptUser().CantonPartyID).Return("0", errors.New("timeout")) svc := token.NewTokenService(newCfg(), provider, userStore, nil) erc20 := token.NewERC20(promptAddr, svc) diff --git a/pkg/token/mocks/mock_canton_token.go b/pkg/token/mocks/mock_canton_token.go index 33090d3..db4b29a 100644 --- a/pkg/token/mocks/mock_canton_token.go +++ b/pkg/token/mocks/mock_canton_token.go @@ -232,6 +232,64 @@ func (_c *Token_GetBalanceByFingerprint_Call) RunAndReturn(run func(context.Cont return _c } +// GetBalanceByPartyID provides a mock function with given fields: ctx, partyID, tokenSymbol +func (_m *Token) GetBalanceByPartyID(ctx context.Context, partyID string, tokenSymbol string) (string, error) { + ret := _m.Called(ctx, partyID, tokenSymbol) + + if len(ret) == 0 { + panic("no return value specified for GetBalanceByPartyID") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (string, error)); ok { + return rf(ctx, partyID, tokenSymbol) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) string); ok { + r0 = rf(ctx, partyID, tokenSymbol) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, partyID, tokenSymbol) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Token_GetBalanceByPartyID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBalanceByPartyID' +type Token_GetBalanceByPartyID_Call struct { + *mock.Call +} + +// GetBalanceByPartyID is a helper method to define mock.On call +// - ctx context.Context +// - partyID string +// - tokenSymbol string +func (_e *Token_Expecter) GetBalanceByPartyID(ctx interface{}, partyID interface{}, tokenSymbol interface{}) *Token_GetBalanceByPartyID_Call { + return &Token_GetBalanceByPartyID_Call{Call: _e.mock.On("GetBalanceByPartyID", ctx, partyID, tokenSymbol)} +} + +func (_c *Token_GetBalanceByPartyID_Call) Run(run func(ctx context.Context, partyID string, tokenSymbol string)) *Token_GetBalanceByPartyID_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *Token_GetBalanceByPartyID_Call) Return(_a0 string, _a1 error) *Token_GetBalanceByPartyID_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Token_GetBalanceByPartyID_Call) RunAndReturn(run func(context.Context, string, string) (string, error)) *Token_GetBalanceByPartyID_Call { + _c.Call.Return(run) + return _c +} + // GetHoldings provides a mock function with given fields: ctx, ownerParty, tokenSymbol func (_m *Token) GetHoldings(ctx context.Context, ownerParty string, tokenSymbol string) ([]*token.Holding, error) { ret := _m.Called(ctx, ownerParty, tokenSymbol) diff --git a/pkg/token/mocks/mock_provider.go b/pkg/token/mocks/mock_provider.go index 2eb1041..7a34bba 100644 --- a/pkg/token/mocks/mock_provider.go +++ b/pkg/token/mocks/mock_provider.go @@ -21,9 +21,9 @@ func (_m *Provider) EXPECT() *Provider_Expecter { return &Provider_Expecter{mock: &_m.Mock} } -// GetBalance provides a mock function with given fields: ctx, tokenSymbol, fingerprint -func (_m *Provider) GetBalance(ctx context.Context, tokenSymbol string, fingerprint string) (string, error) { - ret := _m.Called(ctx, tokenSymbol, fingerprint) +// GetBalance provides a mock function with given fields: ctx, tokenSymbol, partyID +func (_m *Provider) GetBalance(ctx context.Context, tokenSymbol string, partyID string) (string, error) { + ret := _m.Called(ctx, tokenSymbol, partyID) if len(ret) == 0 { panic("no return value specified for GetBalance") @@ -32,16 +32,16 @@ func (_m *Provider) GetBalance(ctx context.Context, tokenSymbol string, fingerpr var r0 string var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) (string, error)); ok { - return rf(ctx, tokenSymbol, fingerprint) + return rf(ctx, tokenSymbol, partyID) } if rf, ok := ret.Get(0).(func(context.Context, string, string) string); ok { - r0 = rf(ctx, tokenSymbol, fingerprint) + r0 = rf(ctx, tokenSymbol, partyID) } else { r0 = ret.Get(0).(string) } if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { - r1 = rf(ctx, tokenSymbol, fingerprint) + r1 = rf(ctx, tokenSymbol, partyID) } else { r1 = ret.Error(1) } @@ -57,12 +57,12 @@ type Provider_GetBalance_Call struct { // GetBalance is a helper method to define mock.On call // - ctx context.Context // - tokenSymbol string -// - fingerprint string -func (_e *Provider_Expecter) GetBalance(ctx interface{}, tokenSymbol interface{}, fingerprint interface{}) *Provider_GetBalance_Call { - return &Provider_GetBalance_Call{Call: _e.mock.On("GetBalance", ctx, tokenSymbol, fingerprint)} +// - partyID string +func (_e *Provider_Expecter) GetBalance(ctx interface{}, tokenSymbol interface{}, partyID interface{}) *Provider_GetBalance_Call { + return &Provider_GetBalance_Call{Call: _e.mock.On("GetBalance", ctx, tokenSymbol, partyID)} } -func (_c *Provider_GetBalance_Call) Run(run func(ctx context.Context, tokenSymbol string, fingerprint string)) *Provider_GetBalance_Call { +func (_c *Provider_GetBalance_Call) Run(run func(ctx context.Context, tokenSymbol string, partyID string)) *Provider_GetBalance_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(string), args[2].(string)) }) diff --git a/pkg/token/provider/canton.go b/pkg/token/provider/canton.go index f18ce13..a5bbaac 100644 --- a/pkg/token/provider/canton.go +++ b/pkg/token/provider/canton.go @@ -18,9 +18,9 @@ func NewCanton(client cantontoken.Token) *Canton { } } -// GetBalance returns token balance by token symbol and user fingerprint. -func (p *Canton) GetBalance(ctx context.Context, tokenSymbol, fingerprint string) (string, error) { - return p.client.GetBalanceByFingerprint(ctx, fingerprint, tokenSymbol) +// GetBalance returns token balance by token symbol and Canton party ID. +func (p *Canton) GetBalance(ctx context.Context, tokenSymbol, partyID string) (string, error) { + return p.client.GetBalanceByPartyID(ctx, partyID, tokenSymbol) } // GetTotalSupply returns token total supply by token symbol. diff --git a/pkg/token/provider/indexer.go b/pkg/token/provider/indexer.go new file mode 100644 index 0000000..f6b12a3 --- /dev/null +++ b/pkg/token/provider/indexer.go @@ -0,0 +1,71 @@ +package provider + +import ( + "context" + "fmt" + + apperrors "github.com/chainsafe/canton-middleware/pkg/app/errors" + indexerclient "github.com/chainsafe/canton-middleware/pkg/indexer/client" +) + +// Indexer implements token.Provider using the indexer's HTTP client. +// +// Unlike the Canton provider — which issues live gRPC ACS scans for every +// balanceOf/totalSupply call — the Indexer provider reads from pre-materialized +// PostgreSQL tables kept current by the indexer's streaming processor. +// +// It is a thin adapter: symbol → admin lookup is handled here; all HTTP +// concerns live in the indexer client. +type Indexer struct { + client indexerclient.Client + // instruments maps tokenSymbol (InstrumentID) → InstrumentAdmin party. + // Required because the indexer keys by (admin, id) but the Provider + // interface only receives the token symbol. + instruments map[string]string +} + +// NewIndexer creates an Indexer-backed token provider. +// +// instruments maps each supported token symbol to its Canton instrument admin +// party string, e.g. map[string]string{"DEMO": "admin::abc123@domain"}. +func NewIndexer(client indexerclient.Client, instruments map[string]string) *Indexer { + return &Indexer{client: client, instruments: instruments} +} + +// GetTotalSupply returns total supply for tokenSymbol via the indexer client. +func (p *Indexer) GetTotalSupply(ctx context.Context, tokenSymbol string) (string, error) { + admin, err := p.instrumentAdmin(tokenSymbol) + if err != nil { + return "0", err + } + return p.client.TotalSupply(ctx, admin, tokenSymbol) +} + +// GetBalance returns the token balance for the given Canton party ID. +// The partyID is resolved upstream in the service layer (from the user record), +// so no additional lookup is needed here. +// A not-found response from the indexer means the party holds zero of this token. +func (p *Indexer) GetBalance(ctx context.Context, tokenSymbol, partyID string) (string, error) { + admin, err := p.instrumentAdmin(tokenSymbol) + if err != nil { + return "0", err + } + b, err := p.client.GetBalance(ctx, partyID, admin, tokenSymbol) + if err != nil { + if apperrors.Is(err, apperrors.CategoryResourceNotFound) { + return "0", nil + } + return "0", err + } + return b.Amount, nil +} + +// instrumentAdmin returns the InstrumentAdmin party for the given token symbol, +// or an error if the symbol is not in the configured instruments map. +func (p *Indexer) instrumentAdmin(tokenSymbol string) (string, error) { + admin, ok := p.instruments[tokenSymbol] + if !ok { + return "", fmt.Errorf("unknown token symbol: %s", tokenSymbol) + } + return admin, nil +} diff --git a/pkg/token/service.go b/pkg/token/service.go index f247cbb..aaea5b7 100644 --- a/pkg/token/service.go +++ b/pkg/token/service.go @@ -25,7 +25,7 @@ type UserStore interface { //go:generate mockery --name Provider --output mocks --outpkg mocks --filename mock_provider.go --with-expecter type Provider interface { GetTotalSupply(ctx context.Context, tokenSymbol string) (string, error) - GetBalance(ctx context.Context, tokenSymbol, fingerprint string) (string, error) + GetBalance(ctx context.Context, tokenSymbol, partyID string) (string, error) } //go:generate mockery --srcpkg github.com/chainsafe/canton-middleware/pkg/cantonsdk/token --name Token --output mocks --outpkg mocks --filename mock_canton_token.go --with-expecter @@ -116,7 +116,7 @@ func (s *Service) getBalance(ctx context.Context, contract, address common.Addre return "0", fmt.Errorf("failed to get user: %w", err) } - return s.provider.GetBalance(ctx, tkn.Symbol, usr.Fingerprint) + return s.provider.GetBalance(ctx, tkn.Symbol, usr.CantonPartyID) } // getTotalSupply returns the total supply for a specific token From bc20e1e7eff94f971da5da8d041689cac7063d99 Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Wed, 8 Apr 2026 14:32:17 +0530 Subject: [PATCH 2/4] mocks generated --- .../service/mocks/mock_pending_block.go | 29 +- pkg/indexer/client/mocks/mock_client.go | 578 ++++++++++++++++++ pkg/relayer/service/mocks/mock_service.go | 155 +++++ pkg/relayer/service/mocks/mock_store.go | 155 +++++ pkg/token/mocks/mock_user_store.go | 51 -- pkg/transfer/mocks/mock_canton_token.go | 58 ++ pkg/user/service/mocks/mock_service.go | 82 +-- pkg/user/service/mocks/mock_topology_cache.go | 72 +-- 8 files changed, 1038 insertions(+), 142 deletions(-) create mode 100644 pkg/indexer/client/mocks/mock_client.go create mode 100644 pkg/relayer/service/mocks/mock_service.go create mode 100644 pkg/relayer/service/mocks/mock_store.go diff --git a/pkg/ethrpc/service/mocks/mock_pending_block.go b/pkg/ethrpc/service/mocks/mock_pending_block.go index 42225fe..52f8710 100644 --- a/pkg/ethrpc/service/mocks/mock_pending_block.go +++ b/pkg/ethrpc/service/mocks/mock_pending_block.go @@ -162,9 +162,9 @@ func (_c *PendingBlock_AddEvmTransaction_Call) RunAndReturn(run func(context.Con return _c } -// ClaimMempoolEntries provides a mock function with given fields: ctx -func (_m *PendingBlock) ClaimMempoolEntries(ctx context.Context) ([]ethrpc.MempoolEntry, error) { - ret := _m.Called(ctx) +// ClaimMempoolEntries provides a mock function with given fields: ctx, maxTxsPerBlock +func (_m *PendingBlock) ClaimMempoolEntries(ctx context.Context, maxTxsPerBlock int) ([]ethrpc.MempoolEntry, error) { + ret := _m.Called(ctx, maxTxsPerBlock) if len(ret) == 0 { panic("no return value specified for ClaimMempoolEntries") @@ -172,19 +172,19 @@ func (_m *PendingBlock) ClaimMempoolEntries(ctx context.Context) ([]ethrpc.Mempo var r0 []ethrpc.MempoolEntry var r1 error - if rf, ok := ret.Get(0).(func(context.Context) ([]ethrpc.MempoolEntry, error)); ok { - return rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, int) ([]ethrpc.MempoolEntry, error)); ok { + return rf(ctx, maxTxsPerBlock) } - if rf, ok := ret.Get(0).(func(context.Context) []ethrpc.MempoolEntry); ok { - r0 = rf(ctx) + if rf, ok := ret.Get(0).(func(context.Context, int) []ethrpc.MempoolEntry); ok { + r0 = rf(ctx, maxTxsPerBlock) } else { if ret.Get(0) != nil { r0 = ret.Get(0).([]ethrpc.MempoolEntry) } } - if rf, ok := ret.Get(1).(func(context.Context) error); ok { - r1 = rf(ctx) + if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { + r1 = rf(ctx, maxTxsPerBlock) } else { r1 = ret.Error(1) } @@ -199,13 +199,14 @@ type PendingBlock_ClaimMempoolEntries_Call struct { // ClaimMempoolEntries is a helper method to define mock.On call // - ctx context.Context -func (_e *PendingBlock_Expecter) ClaimMempoolEntries(ctx interface{}) *PendingBlock_ClaimMempoolEntries_Call { - return &PendingBlock_ClaimMempoolEntries_Call{Call: _e.mock.On("ClaimMempoolEntries", ctx)} +// - maxTxsPerBlock int +func (_e *PendingBlock_Expecter) ClaimMempoolEntries(ctx interface{}, maxTxsPerBlock interface{}) *PendingBlock_ClaimMempoolEntries_Call { + return &PendingBlock_ClaimMempoolEntries_Call{Call: _e.mock.On("ClaimMempoolEntries", ctx, maxTxsPerBlock)} } -func (_c *PendingBlock_ClaimMempoolEntries_Call) Run(run func(ctx context.Context)) *PendingBlock_ClaimMempoolEntries_Call { +func (_c *PendingBlock_ClaimMempoolEntries_Call) Run(run func(ctx context.Context, maxTxsPerBlock int)) *PendingBlock_ClaimMempoolEntries_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) + run(args[0].(context.Context), args[1].(int)) }) return _c } @@ -215,7 +216,7 @@ func (_c *PendingBlock_ClaimMempoolEntries_Call) Return(_a0 []ethrpc.MempoolEntr return _c } -func (_c *PendingBlock_ClaimMempoolEntries_Call) RunAndReturn(run func(context.Context) ([]ethrpc.MempoolEntry, error)) *PendingBlock_ClaimMempoolEntries_Call { +func (_c *PendingBlock_ClaimMempoolEntries_Call) RunAndReturn(run func(context.Context, int) ([]ethrpc.MempoolEntry, error)) *PendingBlock_ClaimMempoolEntries_Call { _c.Call.Return(run) return _c } diff --git a/pkg/indexer/client/mocks/mock_client.go b/pkg/indexer/client/mocks/mock_client.go new file mode 100644 index 0000000..f032d9b --- /dev/null +++ b/pkg/indexer/client/mocks/mock_client.go @@ -0,0 +1,578 @@ +// Code generated by mockery v2.53.6. DO NOT EDIT. + +package mocks + +import ( + context "context" + + indexer "github.com/chainsafe/canton-middleware/pkg/indexer" + mock "github.com/stretchr/testify/mock" +) + +// Client is an autogenerated mock type for the Client type +type Client struct { + mock.Mock +} + +type Client_Expecter struct { + mock *mock.Mock +} + +func (_m *Client) EXPECT() *Client_Expecter { + return &Client_Expecter{mock: &_m.Mock} +} + +// GetBalance provides a mock function with given fields: ctx, partyID, admin, id +func (_m *Client) GetBalance(ctx context.Context, partyID string, admin string, id string) (*indexer.Balance, error) { + ret := _m.Called(ctx, partyID, admin, id) + + if len(ret) == 0 { + panic("no return value specified for GetBalance") + } + + var r0 *indexer.Balance + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) (*indexer.Balance, error)); ok { + return rf(ctx, partyID, admin, id) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, string) *indexer.Balance); ok { + r0 = rf(ctx, partyID, admin, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*indexer.Balance) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, string) error); ok { + r1 = rf(ctx, partyID, admin, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_GetBalance_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBalance' +type Client_GetBalance_Call struct { + *mock.Call +} + +// GetBalance is a helper method to define mock.On call +// - ctx context.Context +// - partyID string +// - admin string +// - id string +func (_e *Client_Expecter) GetBalance(ctx interface{}, partyID interface{}, admin interface{}, id interface{}) *Client_GetBalance_Call { + return &Client_GetBalance_Call{Call: _e.mock.On("GetBalance", ctx, partyID, admin, id)} +} + +func (_c *Client_GetBalance_Call) Run(run func(ctx context.Context, partyID string, admin string, id string)) *Client_GetBalance_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string)) + }) + return _c +} + +func (_c *Client_GetBalance_Call) Return(_a0 *indexer.Balance, _a1 error) *Client_GetBalance_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_GetBalance_Call) RunAndReturn(run func(context.Context, string, string, string) (*indexer.Balance, error)) *Client_GetBalance_Call { + _c.Call.Return(run) + return _c +} + +// GetEvent provides a mock function with given fields: ctx, contractID +func (_m *Client) GetEvent(ctx context.Context, contractID string) (*indexer.ParsedEvent, error) { + ret := _m.Called(ctx, contractID) + + if len(ret) == 0 { + panic("no return value specified for GetEvent") + } + + var r0 *indexer.ParsedEvent + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*indexer.ParsedEvent, error)); ok { + return rf(ctx, contractID) + } + if rf, ok := ret.Get(0).(func(context.Context, string) *indexer.ParsedEvent); ok { + r0 = rf(ctx, contractID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*indexer.ParsedEvent) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, contractID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_GetEvent_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetEvent' +type Client_GetEvent_Call struct { + *mock.Call +} + +// GetEvent is a helper method to define mock.On call +// - ctx context.Context +// - contractID string +func (_e *Client_Expecter) GetEvent(ctx interface{}, contractID interface{}) *Client_GetEvent_Call { + return &Client_GetEvent_Call{Call: _e.mock.On("GetEvent", ctx, contractID)} +} + +func (_c *Client_GetEvent_Call) Run(run func(ctx context.Context, contractID string)) *Client_GetEvent_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *Client_GetEvent_Call) Return(_a0 *indexer.ParsedEvent, _a1 error) *Client_GetEvent_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_GetEvent_Call) RunAndReturn(run func(context.Context, string) (*indexer.ParsedEvent, error)) *Client_GetEvent_Call { + _c.Call.Return(run) + return _c +} + +// GetToken provides a mock function with given fields: ctx, admin, id +func (_m *Client) GetToken(ctx context.Context, admin string, id string) (*indexer.Token, error) { + ret := _m.Called(ctx, admin, id) + + if len(ret) == 0 { + panic("no return value specified for GetToken") + } + + var r0 *indexer.Token + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (*indexer.Token, error)); ok { + return rf(ctx, admin, id) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) *indexer.Token); ok { + r0 = rf(ctx, admin, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*indexer.Token) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, admin, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_GetToken_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetToken' +type Client_GetToken_Call struct { + *mock.Call +} + +// GetToken is a helper method to define mock.On call +// - ctx context.Context +// - admin string +// - id string +func (_e *Client_Expecter) GetToken(ctx interface{}, admin interface{}, id interface{}) *Client_GetToken_Call { + return &Client_GetToken_Call{Call: _e.mock.On("GetToken", ctx, admin, id)} +} + +func (_c *Client_GetToken_Call) Run(run func(ctx context.Context, admin string, id string)) *Client_GetToken_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *Client_GetToken_Call) Return(_a0 *indexer.Token, _a1 error) *Client_GetToken_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_GetToken_Call) RunAndReturn(run func(context.Context, string, string) (*indexer.Token, error)) *Client_GetToken_Call { + _c.Call.Return(run) + return _c +} + +// ListBalancesForParty provides a mock function with given fields: ctx, partyID, p +func (_m *Client) ListBalancesForParty(ctx context.Context, partyID string, p indexer.Pagination) (*indexer.Page[*indexer.Balance], error) { + ret := _m.Called(ctx, partyID, p) + + if len(ret) == 0 { + panic("no return value specified for ListBalancesForParty") + } + + var r0 *indexer.Page[*indexer.Balance] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, indexer.Pagination) (*indexer.Page[*indexer.Balance], error)); ok { + return rf(ctx, partyID, p) + } + if rf, ok := ret.Get(0).(func(context.Context, string, indexer.Pagination) *indexer.Page[*indexer.Balance]); ok { + r0 = rf(ctx, partyID, p) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*indexer.Page[*indexer.Balance]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, indexer.Pagination) error); ok { + r1 = rf(ctx, partyID, p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_ListBalancesForParty_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListBalancesForParty' +type Client_ListBalancesForParty_Call struct { + *mock.Call +} + +// ListBalancesForParty is a helper method to define mock.On call +// - ctx context.Context +// - partyID string +// - p indexer.Pagination +func (_e *Client_Expecter) ListBalancesForParty(ctx interface{}, partyID interface{}, p interface{}) *Client_ListBalancesForParty_Call { + return &Client_ListBalancesForParty_Call{Call: _e.mock.On("ListBalancesForParty", ctx, partyID, p)} +} + +func (_c *Client_ListBalancesForParty_Call) Run(run func(ctx context.Context, partyID string, p indexer.Pagination)) *Client_ListBalancesForParty_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(indexer.Pagination)) + }) + return _c +} + +func (_c *Client_ListBalancesForParty_Call) Return(_a0 *indexer.Page[*indexer.Balance], _a1 error) *Client_ListBalancesForParty_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_ListBalancesForParty_Call) RunAndReturn(run func(context.Context, string, indexer.Pagination) (*indexer.Page[*indexer.Balance], error)) *Client_ListBalancesForParty_Call { + _c.Call.Return(run) + return _c +} + +// ListBalancesForToken provides a mock function with given fields: ctx, admin, id, p +func (_m *Client) ListBalancesForToken(ctx context.Context, admin string, id string, p indexer.Pagination) (*indexer.Page[*indexer.Balance], error) { + ret := _m.Called(ctx, admin, id, p) + + if len(ret) == 0 { + panic("no return value specified for ListBalancesForToken") + } + + var r0 *indexer.Page[*indexer.Balance] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, indexer.Pagination) (*indexer.Page[*indexer.Balance], error)); ok { + return rf(ctx, admin, id, p) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, indexer.Pagination) *indexer.Page[*indexer.Balance]); ok { + r0 = rf(ctx, admin, id, p) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*indexer.Page[*indexer.Balance]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, indexer.Pagination) error); ok { + r1 = rf(ctx, admin, id, p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_ListBalancesForToken_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListBalancesForToken' +type Client_ListBalancesForToken_Call struct { + *mock.Call +} + +// ListBalancesForToken is a helper method to define mock.On call +// - ctx context.Context +// - admin string +// - id string +// - p indexer.Pagination +func (_e *Client_Expecter) ListBalancesForToken(ctx interface{}, admin interface{}, id interface{}, p interface{}) *Client_ListBalancesForToken_Call { + return &Client_ListBalancesForToken_Call{Call: _e.mock.On("ListBalancesForToken", ctx, admin, id, p)} +} + +func (_c *Client_ListBalancesForToken_Call) Run(run func(ctx context.Context, admin string, id string, p indexer.Pagination)) *Client_ListBalancesForToken_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(indexer.Pagination)) + }) + return _c +} + +func (_c *Client_ListBalancesForToken_Call) Return(_a0 *indexer.Page[*indexer.Balance], _a1 error) *Client_ListBalancesForToken_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_ListBalancesForToken_Call) RunAndReturn(run func(context.Context, string, string, indexer.Pagination) (*indexer.Page[*indexer.Balance], error)) *Client_ListBalancesForToken_Call { + _c.Call.Return(run) + return _c +} + +// ListPartyEvents provides a mock function with given fields: ctx, partyID, f, p +func (_m *Client) ListPartyEvents(ctx context.Context, partyID string, f indexer.EventFilter, p indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error) { + ret := _m.Called(ctx, partyID, f, p) + + if len(ret) == 0 { + panic("no return value specified for ListPartyEvents") + } + + var r0 *indexer.Page[*indexer.ParsedEvent] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, indexer.EventFilter, indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error)); ok { + return rf(ctx, partyID, f, p) + } + if rf, ok := ret.Get(0).(func(context.Context, string, indexer.EventFilter, indexer.Pagination) *indexer.Page[*indexer.ParsedEvent]); ok { + r0 = rf(ctx, partyID, f, p) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*indexer.Page[*indexer.ParsedEvent]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, indexer.EventFilter, indexer.Pagination) error); ok { + r1 = rf(ctx, partyID, f, p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_ListPartyEvents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListPartyEvents' +type Client_ListPartyEvents_Call struct { + *mock.Call +} + +// ListPartyEvents is a helper method to define mock.On call +// - ctx context.Context +// - partyID string +// - f indexer.EventFilter +// - p indexer.Pagination +func (_e *Client_Expecter) ListPartyEvents(ctx interface{}, partyID interface{}, f interface{}, p interface{}) *Client_ListPartyEvents_Call { + return &Client_ListPartyEvents_Call{Call: _e.mock.On("ListPartyEvents", ctx, partyID, f, p)} +} + +func (_c *Client_ListPartyEvents_Call) Run(run func(ctx context.Context, partyID string, f indexer.EventFilter, p indexer.Pagination)) *Client_ListPartyEvents_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(indexer.EventFilter), args[3].(indexer.Pagination)) + }) + return _c +} + +func (_c *Client_ListPartyEvents_Call) Return(_a0 *indexer.Page[*indexer.ParsedEvent], _a1 error) *Client_ListPartyEvents_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_ListPartyEvents_Call) RunAndReturn(run func(context.Context, string, indexer.EventFilter, indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error)) *Client_ListPartyEvents_Call { + _c.Call.Return(run) + return _c +} + +// ListTokenEvents provides a mock function with given fields: ctx, admin, id, f, p +func (_m *Client) ListTokenEvents(ctx context.Context, admin string, id string, f indexer.EventFilter, p indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error) { + ret := _m.Called(ctx, admin, id, f, p) + + if len(ret) == 0 { + panic("no return value specified for ListTokenEvents") + } + + var r0 *indexer.Page[*indexer.ParsedEvent] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, indexer.EventFilter, indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error)); ok { + return rf(ctx, admin, id, f, p) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, indexer.EventFilter, indexer.Pagination) *indexer.Page[*indexer.ParsedEvent]); ok { + r0 = rf(ctx, admin, id, f, p) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*indexer.Page[*indexer.ParsedEvent]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, indexer.EventFilter, indexer.Pagination) error); ok { + r1 = rf(ctx, admin, id, f, p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_ListTokenEvents_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListTokenEvents' +type Client_ListTokenEvents_Call struct { + *mock.Call +} + +// ListTokenEvents is a helper method to define mock.On call +// - ctx context.Context +// - admin string +// - id string +// - f indexer.EventFilter +// - p indexer.Pagination +func (_e *Client_Expecter) ListTokenEvents(ctx interface{}, admin interface{}, id interface{}, f interface{}, p interface{}) *Client_ListTokenEvents_Call { + return &Client_ListTokenEvents_Call{Call: _e.mock.On("ListTokenEvents", ctx, admin, id, f, p)} +} + +func (_c *Client_ListTokenEvents_Call) Run(run func(ctx context.Context, admin string, id string, f indexer.EventFilter, p indexer.Pagination)) *Client_ListTokenEvents_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(indexer.EventFilter), args[4].(indexer.Pagination)) + }) + return _c +} + +func (_c *Client_ListTokenEvents_Call) Return(_a0 *indexer.Page[*indexer.ParsedEvent], _a1 error) *Client_ListTokenEvents_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_ListTokenEvents_Call) RunAndReturn(run func(context.Context, string, string, indexer.EventFilter, indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error)) *Client_ListTokenEvents_Call { + _c.Call.Return(run) + return _c +} + +// ListTokens provides a mock function with given fields: ctx, p +func (_m *Client) ListTokens(ctx context.Context, p indexer.Pagination) (*indexer.Page[*indexer.Token], error) { + ret := _m.Called(ctx, p) + + if len(ret) == 0 { + panic("no return value specified for ListTokens") + } + + var r0 *indexer.Page[*indexer.Token] + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, indexer.Pagination) (*indexer.Page[*indexer.Token], error)); ok { + return rf(ctx, p) + } + if rf, ok := ret.Get(0).(func(context.Context, indexer.Pagination) *indexer.Page[*indexer.Token]); ok { + r0 = rf(ctx, p) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*indexer.Page[*indexer.Token]) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, indexer.Pagination) error); ok { + r1 = rf(ctx, p) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_ListTokens_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListTokens' +type Client_ListTokens_Call struct { + *mock.Call +} + +// ListTokens is a helper method to define mock.On call +// - ctx context.Context +// - p indexer.Pagination +func (_e *Client_Expecter) ListTokens(ctx interface{}, p interface{}) *Client_ListTokens_Call { + return &Client_ListTokens_Call{Call: _e.mock.On("ListTokens", ctx, p)} +} + +func (_c *Client_ListTokens_Call) Run(run func(ctx context.Context, p indexer.Pagination)) *Client_ListTokens_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(indexer.Pagination)) + }) + return _c +} + +func (_c *Client_ListTokens_Call) Return(_a0 *indexer.Page[*indexer.Token], _a1 error) *Client_ListTokens_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_ListTokens_Call) RunAndReturn(run func(context.Context, indexer.Pagination) (*indexer.Page[*indexer.Token], error)) *Client_ListTokens_Call { + _c.Call.Return(run) + return _c +} + +// TotalSupply provides a mock function with given fields: ctx, admin, id +func (_m *Client) TotalSupply(ctx context.Context, admin string, id string) (string, error) { + ret := _m.Called(ctx, admin, id) + + if len(ret) == 0 { + panic("no return value specified for TotalSupply") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (string, error)); ok { + return rf(ctx, admin, id) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) string); ok { + r0 = rf(ctx, admin, id) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, admin, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Client_TotalSupply_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TotalSupply' +type Client_TotalSupply_Call struct { + *mock.Call +} + +// TotalSupply is a helper method to define mock.On call +// - ctx context.Context +// - admin string +// - id string +func (_e *Client_Expecter) TotalSupply(ctx interface{}, admin interface{}, id interface{}) *Client_TotalSupply_Call { + return &Client_TotalSupply_Call{Call: _e.mock.On("TotalSupply", ctx, admin, id)} +} + +func (_c *Client_TotalSupply_Call) Run(run func(ctx context.Context, admin string, id string)) *Client_TotalSupply_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *Client_TotalSupply_Call) Return(_a0 string, _a1 error) *Client_TotalSupply_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Client_TotalSupply_Call) RunAndReturn(run func(context.Context, string, string) (string, error)) *Client_TotalSupply_Call { + _c.Call.Return(run) + return _c +} + +// NewClient creates a new instance of Client. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewClient(t interface { + mock.TestingT + Cleanup(func()) +}) *Client { + mock := &Client{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/relayer/service/mocks/mock_service.go b/pkg/relayer/service/mocks/mock_service.go new file mode 100644 index 0000000..31322ff --- /dev/null +++ b/pkg/relayer/service/mocks/mock_service.go @@ -0,0 +1,155 @@ +// Code generated by mockery v2.53.6. DO NOT EDIT. + +package mocks + +import ( + context "context" + + relayer "github.com/chainsafe/canton-middleware/pkg/relayer" + mock "github.com/stretchr/testify/mock" +) + +// Service is an autogenerated mock type for the Service type +type Service struct { + mock.Mock +} + +type Service_Expecter struct { + mock *mock.Mock +} + +func (_m *Service) EXPECT() *Service_Expecter { + return &Service_Expecter{mock: &_m.Mock} +} + +// GetTransfer provides a mock function with given fields: ctx, id +func (_m *Service) GetTransfer(ctx context.Context, id string) (*relayer.Transfer, error) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for GetTransfer") + } + + var r0 *relayer.Transfer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*relayer.Transfer, error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, string) *relayer.Transfer); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*relayer.Transfer) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Service_GetTransfer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTransfer' +type Service_GetTransfer_Call struct { + *mock.Call +} + +// GetTransfer is a helper method to define mock.On call +// - ctx context.Context +// - id string +func (_e *Service_Expecter) GetTransfer(ctx interface{}, id interface{}) *Service_GetTransfer_Call { + return &Service_GetTransfer_Call{Call: _e.mock.On("GetTransfer", ctx, id)} +} + +func (_c *Service_GetTransfer_Call) Run(run func(ctx context.Context, id string)) *Service_GetTransfer_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *Service_GetTransfer_Call) Return(_a0 *relayer.Transfer, _a1 error) *Service_GetTransfer_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Service_GetTransfer_Call) RunAndReturn(run func(context.Context, string) (*relayer.Transfer, error)) *Service_GetTransfer_Call { + _c.Call.Return(run) + return _c +} + +// ListTransfers provides a mock function with given fields: ctx, limit +func (_m *Service) ListTransfers(ctx context.Context, limit int) ([]*relayer.Transfer, error) { + ret := _m.Called(ctx, limit) + + if len(ret) == 0 { + panic("no return value specified for ListTransfers") + } + + var r0 []*relayer.Transfer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*relayer.Transfer, error)); ok { + return rf(ctx, limit) + } + if rf, ok := ret.Get(0).(func(context.Context, int) []*relayer.Transfer); ok { + r0 = rf(ctx, limit) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*relayer.Transfer) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { + r1 = rf(ctx, limit) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Service_ListTransfers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListTransfers' +type Service_ListTransfers_Call struct { + *mock.Call +} + +// ListTransfers is a helper method to define mock.On call +// - ctx context.Context +// - limit int +func (_e *Service_Expecter) ListTransfers(ctx interface{}, limit interface{}) *Service_ListTransfers_Call { + return &Service_ListTransfers_Call{Call: _e.mock.On("ListTransfers", ctx, limit)} +} + +func (_c *Service_ListTransfers_Call) Run(run func(ctx context.Context, limit int)) *Service_ListTransfers_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int)) + }) + return _c +} + +func (_c *Service_ListTransfers_Call) Return(_a0 []*relayer.Transfer, _a1 error) *Service_ListTransfers_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Service_ListTransfers_Call) RunAndReturn(run func(context.Context, int) ([]*relayer.Transfer, error)) *Service_ListTransfers_Call { + _c.Call.Return(run) + return _c +} + +// NewService creates a new instance of Service. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewService(t interface { + mock.TestingT + Cleanup(func()) +}) *Service { + mock := &Service{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/relayer/service/mocks/mock_store.go b/pkg/relayer/service/mocks/mock_store.go new file mode 100644 index 0000000..fe09640 --- /dev/null +++ b/pkg/relayer/service/mocks/mock_store.go @@ -0,0 +1,155 @@ +// Code generated by mockery v2.53.6. DO NOT EDIT. + +package mocks + +import ( + context "context" + + relayer "github.com/chainsafe/canton-middleware/pkg/relayer" + mock "github.com/stretchr/testify/mock" +) + +// Store is an autogenerated mock type for the Store type +type Store struct { + mock.Mock +} + +type Store_Expecter struct { + mock *mock.Mock +} + +func (_m *Store) EXPECT() *Store_Expecter { + return &Store_Expecter{mock: &_m.Mock} +} + +// GetTransfer provides a mock function with given fields: ctx, id +func (_m *Store) GetTransfer(ctx context.Context, id string) (*relayer.Transfer, error) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for GetTransfer") + } + + var r0 *relayer.Transfer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (*relayer.Transfer, error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, string) *relayer.Transfer); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*relayer.Transfer) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_GetTransfer_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetTransfer' +type Store_GetTransfer_Call struct { + *mock.Call +} + +// GetTransfer is a helper method to define mock.On call +// - ctx context.Context +// - id string +func (_e *Store_Expecter) GetTransfer(ctx interface{}, id interface{}) *Store_GetTransfer_Call { + return &Store_GetTransfer_Call{Call: _e.mock.On("GetTransfer", ctx, id)} +} + +func (_c *Store_GetTransfer_Call) Run(run func(ctx context.Context, id string)) *Store_GetTransfer_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *Store_GetTransfer_Call) Return(_a0 *relayer.Transfer, _a1 error) *Store_GetTransfer_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_GetTransfer_Call) RunAndReturn(run func(context.Context, string) (*relayer.Transfer, error)) *Store_GetTransfer_Call { + _c.Call.Return(run) + return _c +} + +// ListTransfers provides a mock function with given fields: ctx, limit +func (_m *Store) ListTransfers(ctx context.Context, limit int) ([]*relayer.Transfer, error) { + ret := _m.Called(ctx, limit) + + if len(ret) == 0 { + panic("no return value specified for ListTransfers") + } + + var r0 []*relayer.Transfer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, int) ([]*relayer.Transfer, error)); ok { + return rf(ctx, limit) + } + if rf, ok := ret.Get(0).(func(context.Context, int) []*relayer.Transfer); ok { + r0 = rf(ctx, limit) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*relayer.Transfer) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { + r1 = rf(ctx, limit) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Store_ListTransfers_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'ListTransfers' +type Store_ListTransfers_Call struct { + *mock.Call +} + +// ListTransfers is a helper method to define mock.On call +// - ctx context.Context +// - limit int +func (_e *Store_Expecter) ListTransfers(ctx interface{}, limit interface{}) *Store_ListTransfers_Call { + return &Store_ListTransfers_Call{Call: _e.mock.On("ListTransfers", ctx, limit)} +} + +func (_c *Store_ListTransfers_Call) Run(run func(ctx context.Context, limit int)) *Store_ListTransfers_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(int)) + }) + return _c +} + +func (_c *Store_ListTransfers_Call) Return(_a0 []*relayer.Transfer, _a1 error) *Store_ListTransfers_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Store_ListTransfers_Call) RunAndReturn(run func(context.Context, int) ([]*relayer.Transfer, error)) *Store_ListTransfers_Call { + _c.Call.Return(run) + return _c +} + +// NewStore creates a new instance of Store. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewStore(t interface { + mock.TestingT + Cleanup(func()) +}) *Store { + mock := &Store{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/token/mocks/mock_user_store.go b/pkg/token/mocks/mock_user_store.go index 0ea43b8..33cd184 100644 --- a/pkg/token/mocks/mock_user_store.go +++ b/pkg/token/mocks/mock_user_store.go @@ -5,7 +5,6 @@ package mocks import ( context "context" - token "github.com/chainsafe/canton-middleware/pkg/token" mock "github.com/stretchr/testify/mock" user "github.com/chainsafe/canton-middleware/pkg/user" @@ -142,56 +141,6 @@ func (_c *UserStore_GetUserByEVMAddress_Call) RunAndReturn(run func(context.Cont return _c } -// TransferBalanceByFingerprint provides a mock function with given fields: ctx, fromFingerprint, toFingerprint, amount, tokenType -func (_m *UserStore) TransferBalanceByFingerprint(ctx context.Context, fromFingerprint string, toFingerprint string, amount string, tokenType token.Type) error { - ret := _m.Called(ctx, fromFingerprint, toFingerprint, amount, tokenType) - - if len(ret) == 0 { - panic("no return value specified for TransferBalanceByFingerprint") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, string, string, token.Type) error); ok { - r0 = rf(ctx, fromFingerprint, toFingerprint, amount, tokenType) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// UserStore_TransferBalanceByFingerprint_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'TransferBalanceByFingerprint' -type UserStore_TransferBalanceByFingerprint_Call struct { - *mock.Call -} - -// TransferBalanceByFingerprint is a helper method to define mock.On call -// - ctx context.Context -// - fromFingerprint string -// - toFingerprint string -// - amount string -// - tokenType token.Type -func (_e *UserStore_Expecter) TransferBalanceByFingerprint(ctx interface{}, fromFingerprint interface{}, toFingerprint interface{}, amount interface{}, tokenType interface{}) *UserStore_TransferBalanceByFingerprint_Call { - return &UserStore_TransferBalanceByFingerprint_Call{Call: _e.mock.On("TransferBalanceByFingerprint", ctx, fromFingerprint, toFingerprint, amount, tokenType)} -} - -func (_c *UserStore_TransferBalanceByFingerprint_Call) Run(run func(ctx context.Context, fromFingerprint string, toFingerprint string, amount string, tokenType token.Type)) *UserStore_TransferBalanceByFingerprint_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context), args[1].(string), args[2].(string), args[3].(string), args[4].(token.Type)) - }) - return _c -} - -func (_c *UserStore_TransferBalanceByFingerprint_Call) Return(_a0 error) *UserStore_TransferBalanceByFingerprint_Call { - _c.Call.Return(_a0) - return _c -} - -func (_c *UserStore_TransferBalanceByFingerprint_Call) RunAndReturn(run func(context.Context, string, string, string, token.Type) error) *UserStore_TransferBalanceByFingerprint_Call { - _c.Call.Return(run) - return _c -} - // NewUserStore creates a new instance of UserStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewUserStore(t interface { diff --git a/pkg/transfer/mocks/mock_canton_token.go b/pkg/transfer/mocks/mock_canton_token.go index 33090d3..db4b29a 100644 --- a/pkg/transfer/mocks/mock_canton_token.go +++ b/pkg/transfer/mocks/mock_canton_token.go @@ -232,6 +232,64 @@ func (_c *Token_GetBalanceByFingerprint_Call) RunAndReturn(run func(context.Cont return _c } +// GetBalanceByPartyID provides a mock function with given fields: ctx, partyID, tokenSymbol +func (_m *Token) GetBalanceByPartyID(ctx context.Context, partyID string, tokenSymbol string) (string, error) { + ret := _m.Called(ctx, partyID, tokenSymbol) + + if len(ret) == 0 { + panic("no return value specified for GetBalanceByPartyID") + } + + var r0 string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (string, error)); ok { + return rf(ctx, partyID, tokenSymbol) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) string); ok { + r0 = rf(ctx, partyID, tokenSymbol) + } else { + r0 = ret.Get(0).(string) + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) error); ok { + r1 = rf(ctx, partyID, tokenSymbol) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Token_GetBalanceByPartyID_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBalanceByPartyID' +type Token_GetBalanceByPartyID_Call struct { + *mock.Call +} + +// GetBalanceByPartyID is a helper method to define mock.On call +// - ctx context.Context +// - partyID string +// - tokenSymbol string +func (_e *Token_Expecter) GetBalanceByPartyID(ctx interface{}, partyID interface{}, tokenSymbol interface{}) *Token_GetBalanceByPartyID_Call { + return &Token_GetBalanceByPartyID_Call{Call: _e.mock.On("GetBalanceByPartyID", ctx, partyID, tokenSymbol)} +} + +func (_c *Token_GetBalanceByPartyID_Call) Run(run func(ctx context.Context, partyID string, tokenSymbol string)) *Token_GetBalanceByPartyID_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(string)) + }) + return _c +} + +func (_c *Token_GetBalanceByPartyID_Call) Return(_a0 string, _a1 error) *Token_GetBalanceByPartyID_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Token_GetBalanceByPartyID_Call) RunAndReturn(run func(context.Context, string, string) (string, error)) *Token_GetBalanceByPartyID_Call { + _c.Call.Return(run) + return _c +} + // GetHoldings provides a mock function with given fields: ctx, ownerParty, tokenSymbol func (_m *Token) GetHoldings(ctx context.Context, ownerParty string, tokenSymbol string) ([]*token.Holding, error) { ret := _m.Called(ctx, ownerParty, tokenSymbol) diff --git a/pkg/user/service/mocks/mock_service.go b/pkg/user/service/mocks/mock_service.go index 132ae3a..2c45ce4 100644 --- a/pkg/user/service/mocks/mock_service.go +++ b/pkg/user/service/mocks/mock_service.go @@ -23,24 +23,24 @@ func (_m *Service) EXPECT() *Service_Expecter { return &Service_Expecter{mock: &_m.Mock} } -// RegisterCantonNativeUser provides a mock function with given fields: ctx, req -func (_m *Service) RegisterCantonNativeUser(ctx context.Context, req *user.RegisterRequest) (*user.RegisterResponse, error) { +// PrepareExternalRegistration provides a mock function with given fields: ctx, req +func (_m *Service) PrepareExternalRegistration(ctx context.Context, req *user.RegisterRequest) (*user.PrepareTopologyResponse, error) { ret := _m.Called(ctx, req) if len(ret) == 0 { - panic("no return value specified for RegisterCantonNativeUser") + panic("no return value specified for PrepareExternalRegistration") } - var r0 *user.RegisterResponse + var r0 *user.PrepareTopologyResponse var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *user.RegisterRequest) (*user.RegisterResponse, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, *user.RegisterRequest) (*user.PrepareTopologyResponse, error)); ok { return rf(ctx, req) } - if rf, ok := ret.Get(0).(func(context.Context, *user.RegisterRequest) *user.RegisterResponse); ok { + if rf, ok := ret.Get(0).(func(context.Context, *user.RegisterRequest) *user.PrepareTopologyResponse); ok { r0 = rf(ctx, req) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*user.RegisterResponse) + r0 = ret.Get(0).(*user.PrepareTopologyResponse) } } @@ -53,41 +53,41 @@ func (_m *Service) RegisterCantonNativeUser(ctx context.Context, req *user.Regis return r0, r1 } -// Service_RegisterCantonNativeUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterCantonNativeUser' -type Service_RegisterCantonNativeUser_Call struct { +// Service_PrepareExternalRegistration_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrepareExternalRegistration' +type Service_PrepareExternalRegistration_Call struct { *mock.Call } -// RegisterCantonNativeUser is a helper method to define mock.On call +// PrepareExternalRegistration is a helper method to define mock.On call // - ctx context.Context // - req *user.RegisterRequest -func (_e *Service_Expecter) RegisterCantonNativeUser(ctx interface{}, req interface{}) *Service_RegisterCantonNativeUser_Call { - return &Service_RegisterCantonNativeUser_Call{Call: _e.mock.On("RegisterCantonNativeUser", ctx, req)} +func (_e *Service_Expecter) PrepareExternalRegistration(ctx interface{}, req interface{}) *Service_PrepareExternalRegistration_Call { + return &Service_PrepareExternalRegistration_Call{Call: _e.mock.On("PrepareExternalRegistration", ctx, req)} } -func (_c *Service_RegisterCantonNativeUser_Call) Run(run func(ctx context.Context, req *user.RegisterRequest)) *Service_RegisterCantonNativeUser_Call { +func (_c *Service_PrepareExternalRegistration_Call) Run(run func(ctx context.Context, req *user.RegisterRequest)) *Service_PrepareExternalRegistration_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(*user.RegisterRequest)) }) return _c } -func (_c *Service_RegisterCantonNativeUser_Call) Return(_a0 *user.RegisterResponse, _a1 error) *Service_RegisterCantonNativeUser_Call { +func (_c *Service_PrepareExternalRegistration_Call) Return(_a0 *user.PrepareTopologyResponse, _a1 error) *Service_PrepareExternalRegistration_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *Service_RegisterCantonNativeUser_Call) RunAndReturn(run func(context.Context, *user.RegisterRequest) (*user.RegisterResponse, error)) *Service_RegisterCantonNativeUser_Call { +func (_c *Service_PrepareExternalRegistration_Call) RunAndReturn(run func(context.Context, *user.RegisterRequest) (*user.PrepareTopologyResponse, error)) *Service_PrepareExternalRegistration_Call { _c.Call.Return(run) return _c } -// RegisterWeb3User provides a mock function with given fields: ctx, req -func (_m *Service) RegisterWeb3User(ctx context.Context, req *user.RegisterRequest) (*user.RegisterResponse, error) { +// RegisterCantonNativeUser provides a mock function with given fields: ctx, req +func (_m *Service) RegisterCantonNativeUser(ctx context.Context, req *user.RegisterRequest) (*user.RegisterResponse, error) { ret := _m.Called(ctx, req) if len(ret) == 0 { - panic("no return value specified for RegisterWeb3User") + panic("no return value specified for RegisterCantonNativeUser") } var r0 *user.RegisterResponse @@ -112,53 +112,53 @@ func (_m *Service) RegisterWeb3User(ctx context.Context, req *user.RegisterReque return r0, r1 } -// Service_RegisterWeb3User_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterWeb3User' -type Service_RegisterWeb3User_Call struct { +// Service_RegisterCantonNativeUser_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterCantonNativeUser' +type Service_RegisterCantonNativeUser_Call struct { *mock.Call } -// RegisterWeb3User is a helper method to define mock.On call +// RegisterCantonNativeUser is a helper method to define mock.On call // - ctx context.Context // - req *user.RegisterRequest -func (_e *Service_Expecter) RegisterWeb3User(ctx interface{}, req interface{}) *Service_RegisterWeb3User_Call { - return &Service_RegisterWeb3User_Call{Call: _e.mock.On("RegisterWeb3User", ctx, req)} +func (_e *Service_Expecter) RegisterCantonNativeUser(ctx interface{}, req interface{}) *Service_RegisterCantonNativeUser_Call { + return &Service_RegisterCantonNativeUser_Call{Call: _e.mock.On("RegisterCantonNativeUser", ctx, req)} } -func (_c *Service_RegisterWeb3User_Call) Run(run func(ctx context.Context, req *user.RegisterRequest)) *Service_RegisterWeb3User_Call { +func (_c *Service_RegisterCantonNativeUser_Call) Run(run func(ctx context.Context, req *user.RegisterRequest)) *Service_RegisterCantonNativeUser_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(*user.RegisterRequest)) }) return _c } -func (_c *Service_RegisterWeb3User_Call) Return(_a0 *user.RegisterResponse, _a1 error) *Service_RegisterWeb3User_Call { +func (_c *Service_RegisterCantonNativeUser_Call) Return(_a0 *user.RegisterResponse, _a1 error) *Service_RegisterCantonNativeUser_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *Service_RegisterWeb3User_Call) RunAndReturn(run func(context.Context, *user.RegisterRequest) (*user.RegisterResponse, error)) *Service_RegisterWeb3User_Call { +func (_c *Service_RegisterCantonNativeUser_Call) RunAndReturn(run func(context.Context, *user.RegisterRequest) (*user.RegisterResponse, error)) *Service_RegisterCantonNativeUser_Call { _c.Call.Return(run) return _c } -// PrepareExternalRegistration provides a mock function with given fields: ctx, req -func (_m *Service) PrepareExternalRegistration(ctx context.Context, req *user.RegisterRequest) (*user.PrepareTopologyResponse, error) { +// RegisterWeb3User provides a mock function with given fields: ctx, req +func (_m *Service) RegisterWeb3User(ctx context.Context, req *user.RegisterRequest) (*user.RegisterResponse, error) { ret := _m.Called(ctx, req) if len(ret) == 0 { - panic("no return value specified for PrepareExternalRegistration") + panic("no return value specified for RegisterWeb3User") } - var r0 *user.PrepareTopologyResponse + var r0 *user.RegisterResponse var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *user.RegisterRequest) (*user.PrepareTopologyResponse, error)); ok { + if rf, ok := ret.Get(0).(func(context.Context, *user.RegisterRequest) (*user.RegisterResponse, error)); ok { return rf(ctx, req) } - if rf, ok := ret.Get(0).(func(context.Context, *user.RegisterRequest) *user.PrepareTopologyResponse); ok { + if rf, ok := ret.Get(0).(func(context.Context, *user.RegisterRequest) *user.RegisterResponse); ok { r0 = rf(ctx, req) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(*user.PrepareTopologyResponse) + r0 = ret.Get(0).(*user.RegisterResponse) } } @@ -171,31 +171,31 @@ func (_m *Service) PrepareExternalRegistration(ctx context.Context, req *user.Re return r0, r1 } -// Service_PrepareExternalRegistration_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'PrepareExternalRegistration' -type Service_PrepareExternalRegistration_Call struct { +// Service_RegisterWeb3User_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'RegisterWeb3User' +type Service_RegisterWeb3User_Call struct { *mock.Call } -// PrepareExternalRegistration is a helper method to define mock.On call +// RegisterWeb3User is a helper method to define mock.On call // - ctx context.Context // - req *user.RegisterRequest -func (_e *Service_Expecter) PrepareExternalRegistration(ctx interface{}, req interface{}) *Service_PrepareExternalRegistration_Call { - return &Service_PrepareExternalRegistration_Call{Call: _e.mock.On("PrepareExternalRegistration", ctx, req)} +func (_e *Service_Expecter) RegisterWeb3User(ctx interface{}, req interface{}) *Service_RegisterWeb3User_Call { + return &Service_RegisterWeb3User_Call{Call: _e.mock.On("RegisterWeb3User", ctx, req)} } -func (_c *Service_PrepareExternalRegistration_Call) Run(run func(ctx context.Context, req *user.RegisterRequest)) *Service_PrepareExternalRegistration_Call { +func (_c *Service_RegisterWeb3User_Call) Run(run func(ctx context.Context, req *user.RegisterRequest)) *Service_RegisterWeb3User_Call { _c.Call.Run(func(args mock.Arguments) { run(args[0].(context.Context), args[1].(*user.RegisterRequest)) }) return _c } -func (_c *Service_PrepareExternalRegistration_Call) Return(_a0 *user.PrepareTopologyResponse, _a1 error) *Service_PrepareExternalRegistration_Call { +func (_c *Service_RegisterWeb3User_Call) Return(_a0 *user.RegisterResponse, _a1 error) *Service_RegisterWeb3User_Call { _c.Call.Return(_a0, _a1) return _c } -func (_c *Service_PrepareExternalRegistration_Call) RunAndReturn(run func(context.Context, *user.RegisterRequest) (*user.PrepareTopologyResponse, error)) *Service_PrepareExternalRegistration_Call { +func (_c *Service_RegisterWeb3User_Call) RunAndReturn(run func(context.Context, *user.RegisterRequest) (*user.RegisterResponse, error)) *Service_RegisterWeb3User_Call { _c.Call.Return(run) return _c } diff --git a/pkg/user/service/mocks/mock_topology_cache.go b/pkg/user/service/mocks/mock_topology_cache.go index 018aa61..d6ec6f7 100644 --- a/pkg/user/service/mocks/mock_topology_cache.go +++ b/pkg/user/service/mocks/mock_topology_cache.go @@ -3,9 +3,9 @@ package mocks import ( + identity "github.com/chainsafe/canton-middleware/pkg/cantonsdk/identity" mock "github.com/stretchr/testify/mock" - identity "github.com/chainsafe/canton-middleware/pkg/cantonsdk/identity" user "github.com/chainsafe/canton-middleware/pkg/user" ) @@ -22,41 +22,6 @@ func (_m *TopologyCacheProvider) EXPECT() *TopologyCacheProvider_Expecter { return &TopologyCacheProvider_Expecter{mock: &_m.Mock} } -// Put provides a mock function with given fields: token, topo, spkiKey -func (_m *TopologyCacheProvider) Put(token string, topo *identity.ExternalPartyTopology, spkiKey []byte) { - _m.Called(token, topo, spkiKey) -} - -// TopologyCacheProvider_Put_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Put' -type TopologyCacheProvider_Put_Call struct { - *mock.Call -} - -// Put is a helper method to define mock.On call -// - token string -// - topo *identity.ExternalPartyTopology -// - spkiKey []byte -func (_e *TopologyCacheProvider_Expecter) Put(token interface{}, topo interface{}, spkiKey interface{}) *TopologyCacheProvider_Put_Call { - return &TopologyCacheProvider_Put_Call{Call: _e.mock.On("Put", token, topo, spkiKey)} -} - -func (_c *TopologyCacheProvider_Put_Call) Run(run func(token string, topo *identity.ExternalPartyTopology, spkiKey []byte)) *TopologyCacheProvider_Put_Call { - _c.Call.Run(func(args mock.Arguments) { - run(args[0].(string), args[1].(*identity.ExternalPartyTopology), args[2].([]byte)) - }) - return _c -} - -func (_c *TopologyCacheProvider_Put_Call) Return() *TopologyCacheProvider_Put_Call { - _c.Call.Return() - return _c -} - -func (_c *TopologyCacheProvider_Put_Call) RunAndReturn(run func(string, *identity.ExternalPartyTopology, []byte)) *TopologyCacheProvider_Put_Call { - _c.Run(run) - return _c -} - // GetAndDelete provides a mock function with given fields: token func (_m *TopologyCacheProvider) GetAndDelete(token string) (*user.PendingTopology, error) { ret := _m.Called(token) @@ -115,6 +80,41 @@ func (_c *TopologyCacheProvider_GetAndDelete_Call) RunAndReturn(run func(string) return _c } +// Put provides a mock function with given fields: token, topo, spkiKey +func (_m *TopologyCacheProvider) Put(token string, topo *identity.ExternalPartyTopology, spkiKey []byte) { + _m.Called(token, topo, spkiKey) +} + +// TopologyCacheProvider_Put_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Put' +type TopologyCacheProvider_Put_Call struct { + *mock.Call +} + +// Put is a helper method to define mock.On call +// - token string +// - topo *identity.ExternalPartyTopology +// - spkiKey []byte +func (_e *TopologyCacheProvider_Expecter) Put(token interface{}, topo interface{}, spkiKey interface{}) *TopologyCacheProvider_Put_Call { + return &TopologyCacheProvider_Put_Call{Call: _e.mock.On("Put", token, topo, spkiKey)} +} + +func (_c *TopologyCacheProvider_Put_Call) Run(run func(token string, topo *identity.ExternalPartyTopology, spkiKey []byte)) *TopologyCacheProvider_Put_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(string), args[1].(*identity.ExternalPartyTopology), args[2].([]byte)) + }) + return _c +} + +func (_c *TopologyCacheProvider_Put_Call) Return() *TopologyCacheProvider_Put_Call { + _c.Call.Return() + return _c +} + +func (_c *TopologyCacheProvider_Put_Call) RunAndReturn(run func(string, *identity.ExternalPartyTopology, []byte)) *TopologyCacheProvider_Put_Call { + _c.Run(run) + return _c +} + // NewTopologyCacheProvider creates a new instance of TopologyCacheProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewTopologyCacheProvider(t interface { From 17318959d57c0b661bfb37982e014fa69064c824 Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Wed, 8 Apr 2026 14:48:47 +0530 Subject: [PATCH 3/4] fixes --- pkg/indexer/client/http.go | 29 ++++++++-- pkg/indexer/client/http_test.go | 99 +++++++++++++++++++++++++-------- pkg/token/provider/indexer.go | 2 +- 3 files changed, 101 insertions(+), 29 deletions(-) diff --git a/pkg/indexer/client/http.go b/pkg/indexer/client/http.go index b812ede..6d1a1ea 100644 --- a/pkg/indexer/client/http.go +++ b/pkg/indexer/client/http.go @@ -8,6 +8,7 @@ import ( "net/http" "net/url" "strconv" + "strings" apperrors "github.com/chainsafe/canton-middleware/pkg/app/errors" "github.com/chainsafe/canton-middleware/pkg/indexer" @@ -45,13 +46,23 @@ type HTTP struct { } // New creates an HTTP-backed indexer client. -// baseURL is the indexer's base URL without a trailing slash (e.g. "http://localhost:8080"). +// baseURL is the indexer's base URL (e.g. "http://localhost:8080" or "http://localhost:8080/"). +// The path component is cleaned so that appended paths are never double-slashed. // httpClient may be nil; http.DefaultClient is used in that case. -func New(baseURL string, httpClient *http.Client) *HTTP { +func New(baseURL string, httpClient *http.Client) (*HTTP, error) { + u, err := url.Parse(baseURL) + if err != nil { + return nil, fmt.Errorf("invalid indexer base URL %q: %w", baseURL, err) + } + if u.Scheme != "http" && u.Scheme != "https" { + return nil, fmt.Errorf("invalid indexer base URL %q: scheme must be http or https", baseURL) + } + // Strip trailing slash so every path helper can safely prepend "/...". + u.Path = strings.TrimSuffix(u.Path, "/") if httpClient == nil { httpClient = http.DefaultClient } - return &HTTP{baseURL: baseURL, httpClient: httpClient} + return &HTTP{baseURL: u.String(), httpClient: httpClient}, nil } // GetToken calls GET /indexer/v1/admin/tokens/{admin}/{id}. @@ -189,14 +200,20 @@ func (c *HTTP) getJSON(ctx context.Context, rawURL string, dest any) error { defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { + // Best-effort decode of the indexer's JSON error envelope. + // If the body is not JSON (e.g. an HTML gateway error page), errMsg + // stays empty and the status code alone is returned to the caller. + var errMsg string var body struct { Error string `json:"error"` } - _ = json.NewDecoder(resp.Body).Decode(&body) + if err := json.NewDecoder(resp.Body).Decode(&body); err == nil { + errMsg = body.Error + } if resp.StatusCode == http.StatusNotFound { - return apperrors.ResourceNotFoundError(nil, body.Error) + return apperrors.ResourceNotFoundError(nil, errMsg) } - return fmt.Errorf("indexer HTTP %d: %s", resp.StatusCode, body.Error) + return fmt.Errorf("indexer HTTP %d: %s", resp.StatusCode, errMsg) } if err := json.NewDecoder(resp.Body).Decode(dest); err != nil { diff --git a/pkg/indexer/client/http_test.go b/pkg/indexer/client/http_test.go index 1845080..aff3822 100644 --- a/pkg/indexer/client/http_test.go +++ b/pkg/indexer/client/http_test.go @@ -6,6 +6,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "strings" "testing" "time" @@ -59,6 +60,14 @@ var ( // ── helpers ─────────────────────────────────────────────────────────────────── +// mustNew wraps client.New for use in tests; it fails the test on error. +func mustNew(t *testing.T, baseURL string, httpClient *http.Client) *client.HTTP { + t.Helper() + c, err := client.New(baseURL, httpClient) + require.NoError(t, err) + return c +} + func jsonResp(status int, body any) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -91,7 +100,7 @@ func TestHTTP_GetToken_Success(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.GetToken(context.Background(), admin, id) require.NoError(t, err) @@ -103,7 +112,7 @@ func TestHTTP_GetToken_NotFound(t *testing.T) { srv := httptest.NewServer(errResp(http.StatusNotFound, "token not found")) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) _, err := c.GetToken(context.Background(), admin, id) require.Error(t, err) @@ -121,7 +130,7 @@ func TestHTTP_ListTokens_Success(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.ListTokens(context.Background(), testPagination) require.NoError(t, err) @@ -134,7 +143,7 @@ func TestHTTP_ListTokens_Empty(t *testing.T) { srv := httptest.NewServer(jsonResp(http.StatusOK, page)) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.ListTokens(context.Background(), testPagination) require.NoError(t, err) @@ -150,7 +159,7 @@ func TestHTTP_TotalSupply_Success(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) supply, err := c.TotalSupply(context.Background(), admin, id) require.NoError(t, err) @@ -161,7 +170,7 @@ func TestHTTP_TotalSupply_ServerError(t *testing.T) { srv := httptest.NewServer(errResp(http.StatusInternalServerError, "database unavailable")) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) supply, err := c.TotalSupply(context.Background(), admin, id) assert.Equal(t, "0", supply) @@ -173,7 +182,7 @@ func TestHTTP_TotalSupply_NetworkError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) supply, err := c.TotalSupply(context.Background(), admin, id) assert.Equal(t, "0", supply) @@ -187,7 +196,7 @@ func TestHTTP_TotalSupply_MalformedResponse(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) supply, err := c.TotalSupply(context.Background(), admin, id) assert.Equal(t, "0", supply) @@ -195,6 +204,24 @@ func TestHTTP_TotalSupply_MalformedResponse(t *testing.T) { assert.ErrorContains(t, err, "decode response") } +func TestHTTP_TotalSupply_NonJSONErrorBody(t *testing.T) { + // Gateway/proxy may return an HTML error page instead of JSON. + // The client should still return an error with the status code. + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + w.WriteHeader(http.StatusBadGateway) + _, _ = w.Write([]byte("Bad Gateway")) + })) + defer srv.Close() + + c := mustNew(t, srv.URL, srv.Client()) + supply, err := c.TotalSupply(context.Background(), admin, id) + + assert.Equal(t, "0", supply) + require.Error(t, err) + assert.ErrorContains(t, err, "indexer HTTP 502") +} + // ── GetBalance ──────────────────────────────────────────────────────────────── func TestHTTP_GetBalance_Success(t *testing.T) { @@ -207,7 +234,7 @@ func TestHTTP_GetBalance_Success(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.GetBalance(context.Background(), partyID, admin, id) require.NoError(t, err) @@ -221,7 +248,7 @@ func TestHTTP_GetBalance_NotFound_ReturnsResourceNotFoundError(t *testing.T) { srv := httptest.NewServer(errResp(http.StatusNotFound, "balance not found")) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.GetBalance(context.Background(), partyID, admin, id) assert.Nil(t, got) @@ -233,7 +260,7 @@ func TestHTTP_GetBalance_ServerError(t *testing.T) { srv := httptest.NewServer(errResp(http.StatusBadGateway, "upstream failure")) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.GetBalance(context.Background(), partyID, admin, id) assert.Nil(t, got) @@ -245,7 +272,7 @@ func TestHTTP_GetBalance_NetworkError(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.GetBalance(context.Background(), partyID, admin, id) assert.Nil(t, got) @@ -263,7 +290,7 @@ func TestHTTP_ListBalancesForParty_Success(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.ListBalancesForParty(context.Background(), partyID, testPagination) require.NoError(t, err) @@ -285,7 +312,7 @@ func TestHTTP_ListBalancesForToken_Success(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.ListBalancesForToken(context.Background(), admin, id, testPagination) require.NoError(t, err) @@ -301,7 +328,7 @@ func TestHTTP_GetEvent_Success(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.GetEvent(context.Background(), contractID) require.NoError(t, err) @@ -313,7 +340,7 @@ func TestHTTP_GetEvent_NotFound(t *testing.T) { srv := httptest.NewServer(errResp(http.StatusNotFound, "event not found")) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) _, err := c.GetEvent(context.Background(), contractID) require.Error(t, err) @@ -335,7 +362,7 @@ func TestHTTP_ListTokenEvents_NoFilter(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.ListTokenEvents(context.Background(), admin, id, indexer.EventFilter{}, testPagination) require.NoError(t, err) @@ -350,7 +377,7 @@ func TestHTTP_ListTokenEvents_WithEventTypeFilter(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.ListTokenEvents(context.Background(), admin, id, indexer.EventFilter{EventType: indexer.EventMint}, testPagination) @@ -370,7 +397,7 @@ func TestHTTP_ListPartyEvents_NoFilter(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.ListPartyEvents(context.Background(), partyID, indexer.EventFilter{}, testPagination) require.NoError(t, err) @@ -385,7 +412,7 @@ func TestHTTP_ListPartyEvents_WithEventTypeFilter(t *testing.T) { })) defer srv.Close() - c := client.New(srv.URL, srv.Client()) + c := mustNew(t, srv.URL, srv.Client()) got, err := c.ListPartyEvents(context.Background(), partyID, indexer.EventFilter{EventType: indexer.EventBurn}, testPagination) @@ -393,9 +420,37 @@ func TestHTTP_ListPartyEvents_WithEventTypeFilter(t *testing.T) { assert.Len(t, got.Items, 1) } -// ── NilHTTPClient uses DefaultClient ───────────────────────────────────────── +// ── Constructor ─────────────────────────────────────────────────────────────── func TestNew_NilHTTPClient_UsesDefaultClient(t *testing.T) { - c := client.New("http://localhost:8080", nil) + c := mustNew(t, "http://localhost:8080", nil) require.NotNil(t, c) } + +func TestNew_InvalidURL_ReturnsError(t *testing.T) { + _, err := client.New("://bad-url", nil) + require.Error(t, err) + assert.ErrorContains(t, err, "invalid indexer base URL") +} + +func TestNew_NonHTTPScheme_ReturnsError(t *testing.T) { + _, err := client.New("grpc://localhost:8080", nil) + require.Error(t, err) + assert.ErrorContains(t, err, "scheme must be http or https") +} + +func TestNew_TrailingSlashStripped(t *testing.T) { + // A baseURL supplied with a trailing slash must not produce double-slash paths. + var gotPath string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + gotPath = r.URL.Path + jsonResp(http.StatusOK, map[string]string{"total_supply": "1"})(w, r) + })) + defer srv.Close() + + c := mustNew(t, srv.URL+"/", srv.Client()) // trailing slash + _, err := c.TotalSupply(context.Background(), admin, id) + + require.NoError(t, err) + assert.False(t, strings.HasPrefix(gotPath, "//"), "path should not start with //: %s", gotPath) +} diff --git a/pkg/token/provider/indexer.go b/pkg/token/provider/indexer.go index f6b12a3..3b6248e 100644 --- a/pkg/token/provider/indexer.go +++ b/pkg/token/provider/indexer.go @@ -17,7 +17,7 @@ import ( // It is a thin adapter: symbol → admin lookup is handled here; all HTTP // concerns live in the indexer client. type Indexer struct { - client indexerclient.Client + client indexerclient.Client // instruments maps tokenSymbol (InstrumentID) → InstrumentAdmin party. // Required because the indexer keys by (admin, id) but the Provider // interface only receives the token symbol. From 8e50aaedd6b0f9eeae9d608a6642cec0a386cb98 Mon Sep 17 00:00:00 2001 From: Arun Dhyani Date: Wed, 8 Apr 2026 15:58:30 +0530 Subject: [PATCH 4/4] lint fixes --- pkg/indexer/client/http.go | 28 ++++++++++++++++++++++++---- pkg/indexer/client/http_test.go | 10 +++++----- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/pkg/indexer/client/http.go b/pkg/indexer/client/http.go index 6d1a1ea..5b7d6c8 100644 --- a/pkg/indexer/client/http.go +++ b/pkg/indexer/client/http.go @@ -34,8 +34,18 @@ type Client interface { // Audit trail GetEvent(ctx context.Context, contractID string) (*indexer.ParsedEvent, error) - ListTokenEvents(ctx context.Context, admin, id string, f indexer.EventFilter, p indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error) - ListPartyEvents(ctx context.Context, partyID string, f indexer.EventFilter, p indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error) + ListTokenEvents( + ctx context.Context, + admin, id string, + f indexer.EventFilter, + p indexer.Pagination, + ) (*indexer.Page[*indexer.ParsedEvent], error) + ListPartyEvents( + ctx context.Context, + partyID string, + f indexer.EventFilter, + p indexer.Pagination, + ) (*indexer.Page[*indexer.ParsedEvent], error) } // HTTP implements Client by calling the indexer's unauthenticated admin HTTP API. @@ -139,7 +149,12 @@ func (c *HTTP) GetEvent(ctx context.Context, contractID string) (*indexer.Parsed } // ListTokenEvents calls GET /indexer/v1/admin/tokens/{admin}/{id}/events. -func (c *HTTP) ListTokenEvents(ctx context.Context, admin, id string, f indexer.EventFilter, p indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error) { +func (c *HTTP) ListTokenEvents( + ctx context.Context, + admin, id string, + f indexer.EventFilter, + p indexer.Pagination, +) (*indexer.Page[*indexer.ParsedEvent], error) { q := pageQuery(p) if f.EventType != "" { q.Set("event_type", string(f.EventType)) @@ -153,7 +168,12 @@ func (c *HTTP) ListTokenEvents(ctx context.Context, admin, id string, f indexer. } // ListPartyEvents calls GET /indexer/v1/admin/parties/{partyID}/events. -func (c *HTTP) ListPartyEvents(ctx context.Context, partyID string, f indexer.EventFilter, p indexer.Pagination) (*indexer.Page[*indexer.ParsedEvent], error) { +func (c *HTTP) ListPartyEvents( + ctx context.Context, + partyID string, + f indexer.EventFilter, + p indexer.Pagination, +) (*indexer.Page[*indexer.ParsedEvent], error) { q := pageQuery(p) if f.EventType != "" { q.Set("event_type", string(f.EventType)) diff --git a/pkg/indexer/client/http_test.go b/pkg/indexer/client/http_test.go index aff3822..66403e9 100644 --- a/pkg/indexer/client/http_test.go +++ b/pkg/indexer/client/http_test.go @@ -69,7 +69,7 @@ func mustNew(t *testing.T, baseURL string, httpClient *http.Client) *client.HTTP } func jsonResp(status int, body any) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(body) @@ -179,7 +179,7 @@ func TestHTTP_TotalSupply_ServerError(t *testing.T) { } func TestHTTP_TotalSupply_NetworkError(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + srv := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})) srv.Close() c := mustNew(t, srv.URL, srv.Client()) @@ -190,7 +190,7 @@ func TestHTTP_TotalSupply_NetworkError(t *testing.T) { } func TestHTTP_TotalSupply_MalformedResponse(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("not json")) })) @@ -207,7 +207,7 @@ func TestHTTP_TotalSupply_MalformedResponse(t *testing.T) { func TestHTTP_TotalSupply_NonJSONErrorBody(t *testing.T) { // Gateway/proxy may return an HTML error page instead of JSON. // The client should still return an error with the status code. - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusBadGateway) _, _ = w.Write([]byte("Bad Gateway")) @@ -269,7 +269,7 @@ func TestHTTP_GetBalance_ServerError(t *testing.T) { } func TestHTTP_GetBalance_NetworkError(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + srv := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})) srv.Close() c := mustNew(t, srv.URL, srv.Client())