func TestResendChunks(t *testing.T) {
	req := core.ResendRequest{
		10:   []core.SequenceId{5, 6, 7, 8, 9},
		100:  []core.SequenceId{1},
		2500: []core.SequenceId{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18},
	}
	Convey("The data that comes out of a resend chunk is the same as the data that went into it.", t, func() {
		var config core.Config
		config.MaxChunkDataSize = 10000
		datas := core.MakeResendChunkDatas(&config, req)
		So(len(datas), ShouldEqual, 1)
		parsed, err := core.ParseResendChunkData(datas[0])
		So(err, ShouldBeNil)
		So(parsed, ShouldResemble, req)
	})
	Convey("Resend data can be split across multiple chunks.", t, func() {
		var config core.Config
		config.MaxChunkDataSize = 20
		datas := core.MakeResendChunkDatas(&config, req)
		So(len(datas), ShouldBeGreaterThan, 1)
		merged := make(core.ResendRequest)
		for _, data := range datas {
			parsed, err := core.ParseResendChunkData(data)
			So(err, ShouldBeNil)
			mergeResendData(merged, parsed)
		}
		So(merged, ShouldResemble, req)
	})
	Convey("Malformed resend chunks return errors.", t, func() {
		_, err := core.ParseResendChunkData([]byte{1})
		So(err, ShouldNotBeNil)
	})
}
func TestPositionChunks(t *testing.T) {
	req := core.PositionUpdate{}
	for i := 1; i < 10; i++ {
		req[core.StreamId(i)] = core.SequenceId(i + 1)
	}
	Convey("The data that comes out of a position chunk is the same as the data that went into it.", t, func() {
		var config core.Config
		config.MaxChunkDataSize = 10000
		datas := core.MakePositionChunkDatas(&config, req)
		So(len(datas), ShouldEqual, 1)
		parsed, err := core.ParsePositionChunkData(datas[0])
		So(err, ShouldBeNil)
		So(parsed, ShouldResemble, req)
	})
	Convey("Position data can be split across multiple chunks.", t, func() {
		var config core.Config
		config.MaxChunkDataSize = 20
		datas := core.MakePositionChunkDatas(&config, req)
		So(len(datas), ShouldBeGreaterThan, 1)
		merged := make(core.PositionUpdate)
		for _, data := range datas {
			parsed, err := core.ParsePositionChunkData(data)
			So(err, ShouldBeNil)
			for stream, sequence := range parsed {
				_, ok := merged[stream]
				So(ok, ShouldBeFalse)
				merged[stream] = sequence
			}
		}
		So(merged, ShouldResemble, req)
	})
	Convey("Malformed position chunks return errors.", t, func() {
		_, err := core.ParsePositionChunkData([]byte{1})
		So(err, ShouldNotBeNil)
	})
}
func SequenceTrackerTest(t *testing.T) {
	st := core.MakeSequenceTracker(2345, 77, 10)
	Convey("It remembers its own stream and node ids.", t, func() {
		So(st.StreamId(), ShouldEqual, 2345)
		So(st.NodeId(), ShouldEqual, 77)
	})
	Convey("Everything under maxContiguous is contained by the tracker.", t, func() {
		So(st.StreamId(), ShouldEqual, core.StreamId(2345))
		So(st.Contains(1), ShouldBeTrue)
		So(st.Contains(2), ShouldBeTrue)
		So(st.Contains(4), ShouldBeTrue)
		So(st.Contains(8), ShouldBeTrue)
		So(st.Contains(10), ShouldBeTrue)
		So(st.Contains(11), ShouldBeTrue)
		So(st.Contains(14), ShouldBeTrue)
		So(st.Contains(17), ShouldBeTrue)
		So(st.Contains(100), ShouldBeTrue)
		So(st.Contains(1000), ShouldBeTrue)
	})

	Convey("Scattered sequence ids are identified.", t, func() {
		st.AddSequenceId(12)
		st.AddSequenceId(13)
		st.AddSequenceId(14)
		st.AddSequenceId(20)
		st.AddSequenceId(22)
		sids := map[core.SequenceId]bool{
			10: false,
			11: false,
			12: true,
			13: true,
			14: true,
			15: false,
			16: false,
			17: false,
			18: false,
			19: false,
			20: true,
			21: false,
			22: true,
			23: false,
			24: false,
		}
		So(st.StreamId(), ShouldEqual, core.StreamId(2345))
		verifySequenceTracker(st, 9, sids)
		Convey("and chunkification/dechunkification works", func() {
			var config core.Config
			config.MaxChunkDataSize = 10
			datas := core.MakeSequenceTrackerChunkDatas(&config, st)
			So(len(datas), ShouldBeGreaterThan, 1)
			var sts []*core.SequenceTracker
			for _, data := range datas {
				st, err := core.ParseSequenceTrackerChunkData(data)
				So(err, ShouldBeNil)
				sts = append(sts, st)
			}

			// All trackers should agree on MaxContiguousSequence.
			So(st.MaxContiguousSequence(), ShouldEqual, 10)
			for i := range sts {
				So(sts[i].MaxContiguousSequence(), ShouldEqual, 10)
			}

			// For each scattered sequence id, at least one tracker should have it.  For sequences
			// not contained in the set, none should have it.
			for sequence, has := range sids {
				found := false
				for _, st := range sts {
					found = found || st.Contains(sequence)
				}
				So(found, ShouldEqual, has)
			}
		})
	})

	Convey("Serialization is correct after compaction.", t, func() {
		st.AddSequenceId(12)
		st.AddSequenceId(14)
		st.AddSequenceId(15)
		st.AddSequenceId(10)
		st.AddSequenceId(11) // Should compact up to 11
		st.AddSequenceId(13) // Should compact up to 15
		sids := map[core.SequenceId]bool{
			10: true,
			11: true,
			12: true,
			13: true,
			14: true,
			15: true,
			16: false,
			17: false,
			18: false,
		}
		verifySequenceTracker(st, 15, sids)

		Convey("and chunkification/dechunkification works", func() {
			var config core.Config
			config.MaxChunkDataSize = 10
			datas := core.MakeSequenceTrackerChunkDatas(&config, st)
			So(len(datas), ShouldBeGreaterThan, 1)
			var sts []*core.SequenceTracker
			for _, data := range datas {
				st, err := core.ParseSequenceTrackerChunkData(data)
				So(err, ShouldBeNil)
				sts = append(sts, st)
			}

			// All trackers should agree on MaxContiguousSequence.
			So(st.MaxContiguousSequence(), ShouldEqual, 10)
			for i := range sts {
				So(sts[i].MaxContiguousSequence(), ShouldEqual, 10)
			}

			// For each scattered sequence id, at least one tracker should have it.  For sequences
			// not contained in the set, none should have it.
			for sequence, has := range sids {
				found := false
				for _, st := range sts {
					found = found || st.Contains(sequence)
				}
				So(found, ShouldEqual, has)
			}
		})
	})
}