github.com/status-im/status-go@v1.1.0/centralizedmetrics/metrics_test.go (about)

     1  package centralizedmetrics
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"sync"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/require"
    11  
    12  	"github.com/status-im/status-go/centralizedmetrics/common"
    13  	"github.com/status-im/status-go/protocol/tt"
    14  )
    15  
    16  var testMetric = common.Metric{ID: "user-id", EventName: "test-name", EventValue: map[string]interface{}{"test-name": "test-value"}, Platform: "android", AppVersion: "2.30.0"}
    17  
    18  // TestMetricService covers the main functionalities of MetricService
    19  func TestMetricService(t *testing.T) {
    20  	repository := &TestMetricRepository{}
    21  	processor := &TestMetricProcessor{}
    22  	service := NewMetricService(repository, processor, 1*time.Second)
    23  
    24  	// Start the service
    25  	service.Start()
    26  	defer service.Stop()
    27  
    28  	// Test adding a metric
    29  	if err := service.AddMetric(testMetric); err != nil {
    30  		t.Fatalf("failed to add metric: %v", err)
    31  	}
    32  
    33  	err := tt.RetryWithBackOff(func() error {
    34  		// Verify metrics were processed and deleted
    35  		if len(processor.processedMetrics) != 1 {
    36  			return fmt.Errorf("expected 1 processed metric, got %d", len(processor.processedMetrics))
    37  		}
    38  
    39  		if len(repository.metrics) != 0 {
    40  			return fmt.Errorf("expected 0 metrics in repository, got %d", len(repository.metrics))
    41  		}
    42  		return nil
    43  	})
    44  	require.NoError(t, err)
    45  }
    46  
    47  // TestMetricRepository is a mock implementation of MetricRepository for testing
    48  type TestMetricRepository struct {
    49  	metrics       []common.Metric
    50  	enabled       bool
    51  	userConfirmed bool
    52  	mutex         sync.Mutex
    53  }
    54  
    55  func (r *TestMetricRepository) Poll() ([]common.Metric, error) {
    56  	r.mutex.Lock()
    57  	defer r.mutex.Unlock()
    58  
    59  	polledMetrics := r.metrics
    60  	r.metrics = []common.Metric{}
    61  	return polledMetrics, nil
    62  }
    63  
    64  func (r *TestMetricRepository) ToggleEnabled(enabled bool) error {
    65  	r.enabled = enabled
    66  	return nil
    67  }
    68  
    69  func (r *TestMetricRepository) Info() (*MetricsInfo, error) {
    70  	return &MetricsInfo{Enabled: r.enabled, UserConfirmed: r.userConfirmed}, nil
    71  }
    72  
    73  func (r *TestMetricRepository) Delete(metrics []common.Metric) error {
    74  	r.mutex.Lock()
    75  	defer r.mutex.Unlock()
    76  
    77  	// Simulate deleting from the repository
    78  	for _, metric := range metrics {
    79  		for i, m := range r.metrics {
    80  			if m.ID == metric.ID {
    81  				r.metrics = append(r.metrics[:i], r.metrics[i+1:]...)
    82  				break
    83  			}
    84  		}
    85  	}
    86  	return nil
    87  }
    88  
    89  func (r *TestMetricRepository) Add(metric common.Metric) error {
    90  	r.mutex.Lock()
    91  	defer r.mutex.Unlock()
    92  
    93  	r.metrics = append(r.metrics, metric)
    94  	return nil
    95  }
    96  
    97  // TestMetricProcessor is a mock implementation of MetricProcessor for testing
    98  type TestMetricProcessor struct {
    99  	processedMetrics []common.Metric
   100  	mutex            sync.Mutex
   101  }
   102  
   103  func (p *TestMetricProcessor) Process(metrics []common.Metric) error {
   104  	p.mutex.Lock()
   105  	defer p.mutex.Unlock()
   106  
   107  	p.processedMetrics = append(p.processedMetrics, metrics...)
   108  	return nil
   109  }
   110  
   111  func TestAddMetric(t *testing.T) {
   112  	repository := &TestMetricRepository{}
   113  	processor := &TestMetricProcessor{}
   114  	service := NewMetricService(repository, processor, 1*time.Second)
   115  
   116  	err := service.AddMetric(testMetric)
   117  	if err != nil {
   118  		t.Fatalf("failed to add metric: %v", err)
   119  	}
   120  
   121  	// Verify metric was added to the repository
   122  	if len(repository.metrics) != 1 {
   123  		t.Fatalf("expected 1 metric in repository, got %d", len(repository.metrics))
   124  	}
   125  
   126  	require.Equal(t, testMetric.ID, repository.metrics[0].ID)
   127  	require.Equal(t, testMetric.EventValue, repository.metrics[0].EventValue)
   128  	require.Equal(t, testMetric.Platform, repository.metrics[0].Platform)
   129  	require.Equal(t, testMetric.AppVersion, repository.metrics[0].AppVersion)
   130  }
   131  
   132  func TestProcessMetrics(t *testing.T) {
   133  	repository := &TestMetricRepository{}
   134  	processor := &TestMetricProcessor{}
   135  	service := NewMetricService(repository, processor, 1*time.Second)
   136  
   137  	// Add metrics directly to repository for polling
   138  	require.NoError(t, repository.Add(common.Metric{ID: "3", EventValue: map[string]interface{}{"price": 6.28}}))
   139  	require.NoError(t, repository.Add(common.Metric{ID: "4", EventValue: map[string]interface{}{"price": 2.71}}))
   140  
   141  	service.processMetrics()
   142  
   143  	// Verify metrics were processed
   144  	if len(processor.processedMetrics) != 2 {
   145  		t.Fatalf("expected 2 processed metrics, got %d", len(processor.processedMetrics))
   146  	}
   147  
   148  	// Verify metrics were deleted from repository
   149  	if len(repository.metrics) != 0 {
   150  		t.Fatalf("expected 0 metrics in repository, got %d", len(repository.metrics))
   151  	}
   152  }
   153  
   154  func TestStartStop(t *testing.T) {
   155  	repository := &TestMetricRepository{}
   156  	processor := &TestMetricProcessor{}
   157  	service := NewMetricService(repository, processor, 1*time.Second)
   158  
   159  	service.Start()
   160  	require.True(t, service.started)
   161  	service.Stop()
   162  
   163  	err := tt.RetryWithBackOff(func() error {
   164  		if service.started {
   165  			return errors.New("expected service to be stopped, but it is still running")
   166  		}
   167  		return nil
   168  
   169  	})
   170  	require.NoError(t, err)
   171  }
   172  
   173  func TestServiceWithoutMetrics(t *testing.T) {
   174  	repository := &TestMetricRepository{}
   175  	processor := &TestMetricProcessor{}
   176  	service := NewMetricService(repository, processor, 1*time.Second)
   177  
   178  	service.Start()
   179  	defer service.Stop()
   180  
   181  	// Verify no metrics were processed
   182  	if len(processor.processedMetrics) != 0 {
   183  		t.Fatalf("expected 0 processed metrics, got %d", len(processor.processedMetrics))
   184  	}
   185  }
   186  
   187  func TestServiceEnabled(t *testing.T) {
   188  	repository := &TestMetricRepository{}
   189  	processor := &TestMetricProcessor{}
   190  	service := NewMetricService(repository, processor, 1*time.Second)
   191  
   192  	err := service.ToggleEnabled(true)
   193  	require.NoError(t, err)
   194  	require.True(t, service.started)
   195  
   196  	err = service.ToggleEnabled(false)
   197  	require.NoError(t, err)
   198  	require.False(t, service.started)
   199  }
   200  
   201  func TestServiceEnsureStarted(t *testing.T) {
   202  	repository := &TestMetricRepository{}
   203  	processor := &TestMetricProcessor{}
   204  	service := NewMetricService(repository, processor, 1*time.Second)
   205  
   206  	err := service.EnsureStarted()
   207  	require.NoError(t, err)
   208  	require.False(t, service.started)
   209  
   210  	repository.enabled = true
   211  
   212  	err = service.EnsureStarted()
   213  	require.NoError(t, err)
   214  	require.True(t, service.started)
   215  }