Files
bc-netts-energy/internal/service/energy_service_test.go
2025-11-03 19:26:48 +08:00

184 lines
4.9 KiB
Go

package service
import (
"context"
"errors"
"testing"
"time"
appconfig "github.com/D36u99er/bc-netts-energy/internal/config"
"github.com/D36u99er/bc-netts-energy/internal/netts"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"log/slog"
)
func TestEnsureEnergyAddsAddressAndOrdersCycles(t *testing.T) {
mock := &mockNettsClient{
analyzeData: &netts.AnalyzeUSDTData{
TransferDetails: netts.TransferDetails{
RecommendedEnergy: 131000,
EnergyNeeded: 131000,
},
},
statusResponses: []statusResponse{
{err: netts.ErrAddressNotFound},
{status: &netts.AddressStatus{CyclesRemaining: 2}},
{status: &netts.AddressStatus{CyclesRemaining: 5}},
},
orderResult: &netts.OrderResult{
CyclesPurchased: 3,
TotalCycles: 5,
OrderID: "ORD-123",
Status: "confirmed",
TotalCost: 7.5,
},
}
cfg := appconfig.EnergyConfig{
AutoAddHost: true,
MinCycles: 3,
TargetCycles: 5,
PostOrderWait: appconfig.Duration(0),
DefaultAnalyzeValue: "100.00",
}
logger := slog.New(slog.NewTextHandler(ioDiscard{}, &slog.HandlerOptions{Level: slog.LevelDebug}))
svc := NewEnergyService(cfg, mock, logger)
resp, err := svc.EnsureEnergy(context.Background(), EnsureEnergyRequest{
FromAddress: "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
ToAddress: "TUmyHQNzAkT6SrwThVvay7G4yfGZAyWhmy",
Amount: "50.0",
})
require.NoError(t, err)
assert.True(t, resp.AddressAdded)
assert.Equal(t, 3, resp.CyclesPurchased)
assert.Equal(t, 5, resp.CyclesAfter)
assert.Equal(t, "ORD-123", resp.OrderID)
assert.Equal(t, 131000, resp.RecommendedEnergy)
assert.Equal(t, 131000, resp.EnergyNeeded)
assert.Equal(t, 1, mock.addCalls)
assert.Equal(t, []int{3}, mock.orderCalls)
assert.Equal(t, 0, len(mock.statusResponses))
}
func TestEnsureEnergyFailsWhenAutoAddDisabled(t *testing.T) {
mock := &mockNettsClient{
analyzeData: &netts.AnalyzeUSDTData{TransferDetails: netts.TransferDetails{RecommendedEnergy: 1000}},
statusResponses: []statusResponse{
{err: netts.ErrAddressNotFound},
},
}
cfg := appconfig.EnergyConfig{
AutoAddHost: false,
MinCycles: 2,
TargetCycles: 4,
}
logger := slog.New(slog.NewTextHandler(ioDiscard{}, nil))
svc := NewEnergyService(cfg, mock, logger)
_, err := svc.EnsureEnergy(context.Background(), EnsureEnergyRequest{
FromAddress: "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
ToAddress: "TUmyHQNzAkT6SrwThVvay7G4yfGZAyWhmy",
})
require.Error(t, err)
assert.Equal(t, 0, mock.addCalls)
}
func TestEnsureEnergySkipsOrderWhenSufficientCycles(t *testing.T) {
mock := &mockNettsClient{
analyzeData: &netts.AnalyzeUSDTData{
TransferDetails: netts.TransferDetails{
RecommendedEnergy: 90000,
EnergyNeeded: 80000,
},
},
statusResponses: []statusResponse{
{status: &netts.AddressStatus{CyclesRemaining: 5}},
},
}
cfg := appconfig.EnergyConfig{
AutoAddHost: true,
MinCycles: 3,
TargetCycles: 6,
}
logger := slog.New(slog.NewTextHandler(ioDiscard{}, nil))
svc := NewEnergyService(cfg, mock, logger)
resp, err := svc.EnsureEnergy(context.Background(), EnsureEnergyRequest{
FromAddress: "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
ToAddress: "TUmyHQNzAkT6SrwThVvay7G4yfGZAyWhmy",
})
require.NoError(t, err)
assert.False(t, resp.AddressAdded)
assert.Equal(t, 0, resp.CyclesPurchased)
assert.Equal(t, 5, resp.CyclesAfter)
assert.Empty(t, mock.orderCalls)
}
type statusResponse struct {
status *netts.AddressStatus
err error
}
type mockNettsClient struct {
analyzeData *netts.AnalyzeUSDTData
statusResponses []statusResponse
orderResult *netts.OrderResult
addCalls int
orderCalls []int
}
func (m *mockNettsClient) AnalyzeUSDT(ctx context.Context, req netts.AnalyzeUSDTRequest) (*netts.AnalyzeUSDTData, error) {
if m.analyzeData == nil {
return nil, errors.New("no analysis data")
}
return m.analyzeData, nil
}
func (m *mockNettsClient) GetAddressStatus(ctx context.Context, address string) (*netts.AddressStatus, error) {
if len(m.statusResponses) == 0 {
return nil, errors.New("no status response")
}
resp := m.statusResponses[0]
m.statusResponses = m.statusResponses[1:]
return resp.status, resp.err
}
func (m *mockNettsClient) AddHostAddress(ctx context.Context, address, callback string) (*netts.AddAddressResult, error) {
m.addCalls++
return &netts.AddAddressResult{
Address: address,
CallbackURL: callback,
Timestamp: time.Now().UTC().Format(time.RFC3339),
}, nil
}
func (m *mockNettsClient) OrderCycles(ctx context.Context, address string, cycles int) (*netts.OrderResult, error) {
m.orderCalls = append(m.orderCalls, cycles)
if m.orderResult != nil {
return m.orderResult, nil
}
return &netts.OrderResult{
CyclesPurchased: cycles,
TotalCycles: cycles,
OrderID: "ORD",
Status: "confirmed",
}, nil
}
type ioDiscard struct{}
func (ioDiscard) Write(p []byte) (int, error) { return len(p), nil }