feat: initial Netts energy orchestrator
This commit is contained in:
183
internal/service/energy_service_test.go
Normal file
183
internal/service/energy_service_test.go
Normal file
@@ -0,0 +1,183 @@
|
||||
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 }
|
||||
Reference in New Issue
Block a user