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 range stateEntriesN * 2 {
		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
	var wg sync.WaitGroup
	for range workers {
		wg.Go(func() {
			for range iterations {
				r.state.add(StateEntry{At: time.Now()})
				r.state.getAll()
				r.state.getLast()
			}
		})
	}
	wg.Wait()
}
