382 lines
10 KiB
Go
382 lines
10 KiB
Go
package usecase
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
"users_management/m/model/dto/req"
|
|
"users_management/m/model/entity"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type mockNearestDeviceRepo struct {
|
|
GetNearestDevicesFunc func(longitude, latitude, radius float64, limit int, province, city, district, deviceType *string) ([]entity.DeviceWithDistance, error)
|
|
GetDeviceByIDWithConnectionsFunc func(id uuid.UUID) (entity.Device, error)
|
|
CountConnectionsByDeviceIDFunc func(deviceID uuid.UUID) (int, int, int, error)
|
|
}
|
|
|
|
func (m *mockNearestDeviceRepo) GetNearestDevices(longitude, latitude, radius float64, limit int, province, city, district, deviceType *string) ([]entity.DeviceWithDistance, error) {
|
|
if m.GetNearestDevicesFunc != nil {
|
|
return m.GetNearestDevicesFunc(longitude, latitude, radius, limit, province, city, district, deviceType)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockNearestDeviceRepo) GetDeviceByIDWithConnections(id uuid.UUID) (entity.Device, error) {
|
|
if m.GetDeviceByIDWithConnectionsFunc != nil {
|
|
return m.GetDeviceByIDWithConnectionsFunc(id)
|
|
}
|
|
return entity.Device{}, nil
|
|
}
|
|
|
|
func (m *mockNearestDeviceRepo) GetBackbonesByDeviceID(deviceID uuid.UUID) ([]entity.Backbone, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockNearestDeviceRepo) GetFishbonesByDeviceID(deviceID uuid.UUID) ([]entity.Fishbone, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockNearestDeviceRepo) GetTowersByDeviceID(deviceID uuid.UUID) ([]entity.Tower, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockNearestDeviceRepo) CountConnectionsByDeviceID(deviceID uuid.UUID) (backboneCount, fishboneCount, towerCount int, err error) {
|
|
if m.CountConnectionsByDeviceIDFunc != nil {
|
|
return m.CountConnectionsByDeviceIDFunc(deviceID)
|
|
}
|
|
return 0, 0, 0, nil
|
|
}
|
|
|
|
func (m *mockNearestDeviceRepo) GetNearestTowers(longitude, latitude, radius float64, limit int, province, city, district *string) ([]entity.TowerWithDistance, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (m *mockNearestDeviceRepo) GetTowerByIDWithConnections(id uuid.UUID) (entity.Tower, error) {
|
|
return entity.Tower{}, nil
|
|
}
|
|
|
|
type mockGeocoder struct{}
|
|
|
|
func (m *mockGeocoder) GetAddressFromCoordinates(lat, lng float64) (string, error) {
|
|
return "Mock Address, City", nil
|
|
}
|
|
|
|
func TestGetNearestDevices_Success(t *testing.T) {
|
|
deviceID := uuid.New()
|
|
provinceStr := "Jakarta"
|
|
cityStr := "Jakarta Selatan"
|
|
|
|
mockRepo := &mockNearestDeviceRepo{
|
|
GetNearestDevicesFunc: func(longitude, latitude, radius float64, limit int, prov, cty, district, deviceType *string) ([]entity.DeviceWithDistance, error) {
|
|
if longitude != 106.8 {
|
|
t.Errorf("expected longitude 106.8, got %f", longitude)
|
|
}
|
|
if latitude != -6.2 {
|
|
t.Errorf("expected latitude -6.2, got %f", latitude)
|
|
}
|
|
if radius != 5.0 {
|
|
t.Errorf("expected radius 5.0, got %f", radius)
|
|
}
|
|
if limit != 10 {
|
|
t.Errorf("expected limit 10, got %d", limit)
|
|
}
|
|
_ = prov
|
|
_ = cty
|
|
return []entity.DeviceWithDistance{
|
|
{
|
|
ID: deviceID,
|
|
DeviceCode: "ODP-001",
|
|
DeviceType: entity.ODP,
|
|
Longitude: 106.81,
|
|
Latitude: -6.21,
|
|
PortAmount: 8,
|
|
Status: entity.ActiveDev,
|
|
Province: &provinceStr,
|
|
City: &cityStr,
|
|
Distance: 1.5,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
},
|
|
}, nil
|
|
},
|
|
CountConnectionsByDeviceIDFunc: func(deviceID uuid.UUID) (int, int, int, error) {
|
|
return 2, 3, 1, nil
|
|
},
|
|
}
|
|
|
|
uc := NewNearestDeviceUseCase(mockRepo, &mockGeocoder{})
|
|
|
|
request := req.NearestDeviceDTO{
|
|
Longitude: 106.8,
|
|
Latitude: -6.2,
|
|
Radius: 0,
|
|
Limit: 0,
|
|
}
|
|
|
|
devices, err := uc.GetNearestDevices(request)
|
|
if err != nil {
|
|
t.Fatalf("expected no error, got %v", err)
|
|
}
|
|
|
|
if len(devices) != 1 {
|
|
t.Fatalf("expected 1 device, got %d", len(devices))
|
|
}
|
|
|
|
if devices[0].DeviceCode != "ODP-001" {
|
|
t.Errorf("expected device code ODP-001, got %s", devices[0].DeviceCode)
|
|
}
|
|
}
|
|
|
|
func TestGetNearestDevices_WithDeviceTypeFilter(t *testing.T) {
|
|
deviceType := "ODP"
|
|
|
|
mockRepo := &mockNearestDeviceRepo{
|
|
GetNearestDevicesFunc: func(longitude, latitude, radius float64, limit int, province, city, district, deviceType *string) ([]entity.DeviceWithDistance, error) {
|
|
if deviceType == nil || *deviceType != "ODP" {
|
|
t.Errorf("expected deviceType filter to be ODP")
|
|
}
|
|
return []entity.DeviceWithDistance{
|
|
{
|
|
ID: uuid.New(),
|
|
DeviceCode: "ODP-002",
|
|
DeviceType: entity.ODP,
|
|
Longitude: 106.82,
|
|
Latitude: -6.22,
|
|
PortAmount: 16,
|
|
Status: entity.ActiveDev,
|
|
Distance: 2.5,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
},
|
|
}, nil
|
|
},
|
|
CountConnectionsByDeviceIDFunc: func(deviceID uuid.UUID) (int, int, int, error) {
|
|
return 1, 2, 0, nil
|
|
},
|
|
}
|
|
|
|
uc := NewNearestDeviceUseCase(mockRepo, &mockGeocoder{})
|
|
|
|
request := req.NearestDeviceDTO{
|
|
Longitude: 106.8,
|
|
Latitude: -6.2,
|
|
Radius: 5.0,
|
|
Limit: 10,
|
|
DeviceType: &deviceType,
|
|
}
|
|
|
|
devices, err := uc.GetNearestDevices(request)
|
|
if err != nil {
|
|
t.Fatalf("expected no error, got %v", err)
|
|
}
|
|
|
|
if len(devices) != 1 {
|
|
t.Fatalf("expected 1 device, got %d", len(devices))
|
|
}
|
|
|
|
if devices[0].DeviceType != "ODP" {
|
|
t.Errorf("expected device type ODP, got %s", devices[0].DeviceType)
|
|
}
|
|
}
|
|
|
|
func TestGetNearestDevices_RepositoryError(t *testing.T) {
|
|
mockRepo := &mockNearestDeviceRepo{
|
|
GetNearestDevicesFunc: func(longitude, latitude, radius float64, limit int, province, city, district, deviceType *string) ([]entity.DeviceWithDistance, error) {
|
|
return nil, errors.New("database connection failed")
|
|
},
|
|
}
|
|
|
|
uc := NewNearestDeviceUseCase(mockRepo, &mockGeocoder{})
|
|
|
|
request := req.NearestDeviceDTO{
|
|
Longitude: 106.8,
|
|
Latitude: -6.2,
|
|
}
|
|
|
|
_, err := uc.GetNearestDevices(request)
|
|
if err == nil {
|
|
t.Fatal("expected error, got nil")
|
|
}
|
|
}
|
|
|
|
func TestGetNearestDevices_EmptyResult(t *testing.T) {
|
|
mockRepo := &mockNearestDeviceRepo{
|
|
GetNearestDevicesFunc: func(longitude, latitude, radius float64, limit int, province, city, district, deviceType *string) ([]entity.DeviceWithDistance, error) {
|
|
return []entity.DeviceWithDistance{}, nil
|
|
},
|
|
}
|
|
|
|
uc := NewNearestDeviceUseCase(mockRepo, &mockGeocoder{})
|
|
|
|
request := req.NearestDeviceDTO{
|
|
Longitude: 106.8,
|
|
Latitude: -6.2,
|
|
}
|
|
|
|
devices, err := uc.GetNearestDevices(request)
|
|
if err != nil {
|
|
t.Fatalf("expected no error, got %v", err)
|
|
}
|
|
|
|
if len(devices) != 0 {
|
|
t.Errorf("expected 0 devices, got %d", len(devices))
|
|
}
|
|
}
|
|
|
|
func TestGetNearestDevices_ValidationError(t *testing.T) {
|
|
mockRepo := &mockNearestDeviceRepo{}
|
|
uc := NewNearestDeviceUseCase(mockRepo, &mockGeocoder{})
|
|
|
|
request := req.NearestDeviceDTO{
|
|
Longitude: 0,
|
|
Latitude: 0,
|
|
}
|
|
|
|
_, err := uc.GetNearestDevices(request)
|
|
if err == nil {
|
|
t.Fatal("expected validation error for missing required fields")
|
|
}
|
|
}
|
|
|
|
func TestGetNearestDevices_CustomRadiusAndLimit(t *testing.T) {
|
|
customRadius := 0.3
|
|
customLimit := 5
|
|
|
|
mockRepo := &mockNearestDeviceRepo{
|
|
GetNearestDevicesFunc: func(longitude, latitude, radius float64, limit int, province, city, district, deviceType *string) ([]entity.DeviceWithDistance, error) {
|
|
if radius != customRadius {
|
|
t.Errorf("expected radius %f, got %f", customRadius, radius)
|
|
}
|
|
if limit != customLimit {
|
|
t.Errorf("expected limit %d, got %d", customLimit, limit)
|
|
}
|
|
return []entity.DeviceWithDistance{}, nil
|
|
},
|
|
}
|
|
|
|
uc := NewNearestDeviceUseCase(mockRepo, &mockGeocoder{})
|
|
|
|
request := req.NearestDeviceDTO{
|
|
Longitude: 106.8,
|
|
Latitude: -6.2,
|
|
Radius: customRadius,
|
|
Limit: customLimit,
|
|
}
|
|
|
|
_, err := uc.GetNearestDevices(request)
|
|
if err != nil {
|
|
t.Fatalf("expected no error, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestCalculateDistance(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
lat1 float64
|
|
lng1 float64
|
|
lat2 float64
|
|
lng2 float64
|
|
expected float64
|
|
delta float64
|
|
}{
|
|
{
|
|
name: "Same point",
|
|
lat1: -6.2,
|
|
lng1: 106.8,
|
|
lat2: -6.2,
|
|
lng2: 106.8,
|
|
expected: 0,
|
|
delta: 0.001,
|
|
},
|
|
{
|
|
name: "Jakarta to Bandung",
|
|
lat1: -6.2088,
|
|
lng1: 106.8456,
|
|
lat2: -6.9175,
|
|
lng2: 107.6191,
|
|
expected: 115.0,
|
|
delta: 5.0,
|
|
},
|
|
{
|
|
name: "Short distance ~350m",
|
|
lat1: -6.2,
|
|
lng1: 106.8,
|
|
lat2: -6.20225,
|
|
lng2: 106.80225,
|
|
expected: 0.35,
|
|
delta: 0.05,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := calculateDistance(tt.lat1, tt.lng1, tt.lat2, tt.lng2)
|
|
if result < tt.expected-tt.delta || result > tt.expected+tt.delta {
|
|
t.Errorf("calculateDistance(%f, %f, %f, %f) = %f, expected %f ± %f",
|
|
tt.lat1, tt.lng1, tt.lat2, tt.lng2, result, tt.expected, tt.delta)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetNearestDeviceByID_Success(t *testing.T) {
|
|
deviceID := uuid.New()
|
|
province := "Jakarta"
|
|
city := "Jakarta"
|
|
|
|
mockRepo := &mockNearestDeviceRepo{
|
|
GetDeviceByIDWithConnectionsFunc: func(id uuid.UUID) (entity.Device, error) {
|
|
if id != deviceID {
|
|
t.Errorf("expected device ID %s, got %s", deviceID, id)
|
|
}
|
|
return entity.Device{
|
|
ID: deviceID,
|
|
DeviceCode: "ODP-003",
|
|
DeviceType: entity.ODP,
|
|
Longitude: 106.81,
|
|
Latitude: -6.21,
|
|
PortAmount: 8,
|
|
Status: entity.ActiveDev,
|
|
Province: &province,
|
|
City: &city,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
uc := NewNearestDeviceUseCase(mockRepo, &mockGeocoder{})
|
|
|
|
device, err := uc.GetNearestDeviceByID(deviceID, -6.2, 106.8)
|
|
if err != nil {
|
|
t.Fatalf("expected no error, got %v", err)
|
|
}
|
|
|
|
if device.DeviceCode != "ODP-003" {
|
|
t.Errorf("expected device code ODP-003, got %s", device.DeviceCode)
|
|
}
|
|
|
|
if device.Distance <= 0 {
|
|
t.Errorf("expected positive distance, got %f", device.Distance)
|
|
}
|
|
}
|
|
|
|
func TestGetNearestDeviceByID_NotFound(t *testing.T) {
|
|
deviceID := uuid.New()
|
|
|
|
mockRepo := &mockNearestDeviceRepo{
|
|
GetDeviceByIDWithConnectionsFunc: func(id uuid.UUID) (entity.Device, error) {
|
|
return entity.Device{}, errors.New("record not found")
|
|
},
|
|
}
|
|
|
|
uc := NewNearestDeviceUseCase(mockRepo, &mockGeocoder{})
|
|
|
|
_, err := uc.GetNearestDeviceByID(deviceID, -6.2, 106.8)
|
|
if err == nil {
|
|
t.Fatal("expected error for not found device")
|
|
}
|
|
}
|