package db23

import "testing"

const (
	HashNormal = "5976b45b9f40f440e8249da27fe4fe752e005f606efc3596bdb25ca4e4f99296"
	HashFault  = "d67c36725af65242e985a308db5152af2a3e2525fab33d11ed6e826a252ff792"
)

func TestSha256Vectors(t *testing.T) {
	if Sha256Hex([]byte("")) != "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" {
		t.Fatal("empty")
	}
	if Sha256Hex([]byte("abc")) != "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" {
		t.Fatal("abc")
	}
}

func TestSplitmix64Known(t *testing.T) {
	if Splitmix64(0) != 0x8b57dafca0cee644 {
		t.Fatal("splitmix64(0)")
	}
}

func TestPutThenDelReplicates(t *testing.T) {
	c := NewCluster()
	c.Submit(Op{Kind: OpPut, K: 1, V: 100})
	c.Submit(Op{Kind: OpPut, K: 2, V: 200})
	c.Submit(Op{Kind: OpDel, K: 1})
	for _, n := range c.Nodes {
		if len(n.Log) != 3 || n.CommitIndex != 3 {
			t.Fatalf("node %d log/commit", n.ID)
		}
		if _, ok := n.KV[1]; ok {
			t.Fatal("k=1 should be gone")
		}
		if n.KV[2] != 200 {
			t.Fatal("k=2 != 200")
		}
	}
}

func TestFaultWindowThenCatchupConverges(t *testing.T) {
	c := NewCluster()
	c.Submit(Op{Kind: OpPut, K: 1, V: 1})
	c.SetFollowerUp(1, false)
	c.Submit(Op{Kind: OpPut, K: 2, V: 2})
	c.Submit(Op{Kind: OpPut, K: 3, V: 3})
	if c.Nodes[0].CommitIndex != 3 || c.Nodes[2].CommitIndex != 3 {
		t.Fatal("majority should commit")
	}
	if c.Nodes[1].CommitIndex > 1 {
		t.Fatal("down follower advanced")
	}
	c.SetFollowerUp(1, true)
	c.CatchUp(1)
	for _, n := range c.Nodes {
		if len(n.Log) != 3 || n.CommitIndex != 3 || n.KV[3] != 3 {
			t.Fatalf("node %d not converged", n.ID)
		}
	}
}

func TestSnapshotLayoutSmoke(t *testing.T) {
	c := NewCluster()
	c.Submit(Op{Kind: OpPut, K: 1, V: 42})
	snap := c.EncodeSnapshot()
	if len(snap) != 82*3 {
		t.Fatalf("len=%d want %d", len(snap), 82*3)
	}
	if string(snap[:8]) != "DSEDIST2" {
		t.Fatal("magic")
	}
	if snap[8] != 0 {
		t.Fatal("node 0 id")
	}
}

func TestWorkloadIsDeterministic(t *testing.T) {
	a := Sha256Hex(RunCluster(1, 100, 8, "normal").EncodeSnapshot())
	b := Sha256Hex(RunCluster(1, 100, 8, "normal").EncodeSnapshot())
	if a != b {
		t.Fatal("nondeterministic")
	}
}

func TestFaultScenarioIsDeterministic(t *testing.T) {
	a := Sha256Hex(RunCluster(1, 100, 8, "fault").EncodeSnapshot())
	b := Sha256Hex(RunCluster(1, 100, 8, "fault").EncodeSnapshot())
	if a != b {
		t.Fatal("nondeterministic fault")
	}
}

func TestScenarioNormalFrozen(t *testing.T) {
	got := Sha256Hex(RunCluster(42, 200, 16, "normal").EncodeSnapshot())
	if got != HashNormal {
		t.Fatalf("normal\n got: %s\nwant: %s", got, HashNormal)
	}
}

func TestScenarioFaultFrozen(t *testing.T) {
	got := Sha256Hex(RunCluster(7, 2000, 128, "fault").EncodeSnapshot())
	if got != HashFault {
		t.Fatalf("fault\n got: %s\nwant: %s", got, HashFault)
	}
}
