package storage

import (
	"fmt"
	"os"
	"reflect"
	"testing"
)

type kv struct {
	k, v []byte
}

// TestWorkflow simulates the whole workflow that storage is used in normal
// etcd running, including key changes, compaction and restart.
func TestWorkflow(t *testing.T) {
	s := newStore("test")
	defer os.Remove("test")

	var lastrev int64
	var wkvs []kv
	for i := 0; i < 10; i++ {
		// regular compaction
		s.Compact(lastrev)

		// put 100 keys into the store in each round
		for k := 0; k < 100; k++ {
			key := fmt.Sprintf("bar_%03d_%03d", i, k)
			val := fmt.Sprintf("foo_%03d_%03d", i, k)
			s.Put([]byte(key), []byte(val))
			wkvs = append(wkvs, kv{k: []byte(key), v: []byte(val)})
		}

		// delete second-half keys in this round
		key := fmt.Sprintf("bar_%03d_050", i)
		end := fmt.Sprintf("bar_%03d_100", i)
		if n, _ := s.DeleteRange([]byte(key), []byte(end)); n != 50 {
			t.Errorf("#%d: delete number = %d, want 50", i, n)
		}
		wkvs = wkvs[:len(wkvs)-50]

		// check existing keys
		kvs, rev, err := s.Range([]byte("bar"), []byte("bas"), 0, 0)
		if err != nil {
			t.Errorf("#%d: range error (%v)", err)
		}
		if len(kvs) != len(wkvs) {
			t.Fatalf("#%d: len(kvs) = %d, want %d", i, len(kvs), len(wkvs))
		}
		for j, kv := range kvs {
			if !reflect.DeepEqual(kv.Key, wkvs[j].k) {
				t.Errorf("#%d: keys[%d] = %s, want %s", i, j, kv.Key, wkvs[j].k)
			}
			if !reflect.DeepEqual(kv.Value, wkvs[j].v) {
				t.Errorf("#%d: vals[%d] = %s, want %s", i, j, kv.Value, wkvs[j].v)
			}
		}
		lastrev = rev

		// the store is restarted and restored from the disk file
		s.Close()
		s = newStore("test")
		s.Restore()
	}
}
