package rule

import (
	"sync"
	"testing"
	"time"
)

func TestRule_state(t *testing.T) {
	stateEntriesN := 20
	r := &AlertingRule{state: &ruleState{entries: make([]StateEntry, stateEntriesN)}}
	e := r.state.getLast()
	if !e.At.IsZero() {
		t.Fatalf("expected entry to be zero")
	}

	now := time.Now()
	r.state.add(StateEntry{At: now})

	e = r.state.getLast()
	if e.At != now {
		t.Fatalf("expected entry at %v to be equal to %v",
			e.At, now)
	}

	time.Sleep(time.Millisecond)
	now2 := time.Now()
	r.state.add(StateEntry{At: now2})

	e = r.state.getLast()
	if e.At != now2 {
		t.Fatalf("expected entry at %v to be equal to %v",
			e.At, now2)
	}

	if len(r.state.getAll()) != 2 {
		t.Fatalf("expected for state to have 2 entries only; got %d",
			len(r.state.getAll()),
		)
	}

	var last time.Time
	for i := 0; i < stateEntriesN*2; i++ {
		last = time.Now()
		r.state.add(StateEntry{At: last})
	}

	e = r.state.getLast()
	if e.At != last {
		t.Fatalf("expected entry at %v to be equal to %v",
			e.At, last)
	}

	if len(r.state.getAll()) != stateEntriesN {
		t.Fatalf("expected for state to have %d entries only; got %d",
			stateEntriesN, len(r.state.getAll()),
		)
	}
}

// TestRule_stateConcurrent supposed to test concurrent
// execution of state updates.
// Should be executed with -race flag
func TestRule_stateConcurrent(_ *testing.T) {
	r := &AlertingRule{state: &ruleState{entries: make([]StateEntry, 20)}}
	const workers = 50
	const iterations = 100
	wg := sync.WaitGroup{}
	wg.Add(workers)
	for i := 0; i < workers; i++ {
		go func() {
			defer wg.Done()
			for i := 0; i < iterations; i++ {
				r.state.add(StateEntry{At: time.Now()})
				r.state.getAll()
				r.state.getLast()
			}
		}()
	}
	wg.Wait()
}