package test

import (
	
	
	
	
	
	
	
	
	

	
	

	sst 
	
	amhelp 
	amhelpt 
	am 
	arpc 
	ssrpc 
	
)

var (
	ssC  = ssrpc.ClientStates
	ssCo = ssrpc.ConsumerStates
	ssS  = ssrpc.ServerStates
	ssM  = ssrpc.MuxStates
	Pass = am.Pass
)

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(true)

	// config
	,  := context.WithCancel(context.Background())
	defer ()

	// init worker
	 := ssrpc.StateSourceSchema.Merge(am.Schema{
		"Foo": {},
		"Bar": {Require: am.S{"Foo"}},
	})
	 := am.SAdd(ssrpc.StateSourceStates.Names(), am.S{"Foo", "Bar"})
	 := am.New(, , &am.Opts{Id: "ns-" + .Name()})
	 := .VerifyStates()
	if  != nil {
		.Fatal()
	}

	amhelpt.MachDebugEnv(, )

	// init server and client
	, , ,  := (, , , nil, 0, false, nil, nil)

	// test
	.NetMach.Add1("Foo", nil)

	// assert
	assert.True(, .Mach.Is1(ssrpc.ServerStates.Ready), "Server ready")
	assert.True(, .Mach.Is1(ssrpc.ClientStates.Ready), "Client ready")

	assert.True(, .Mach.Not1(am.StateException), "No server errors")
	assert.True(, .Mach.Not1(am.StateException), "No client errors")

	assert.True(, .Is1("Foo"), "NetworkMachine state set on the server")
	assert.True(, .NetMach.Is1("Foo"), "NetworkMachine state set on the client")

	.Mach.Log("OK")
	.Mach.Log("OK")

	// shut down
	.Mach.Remove1(ssrpc.ClientStates.Start, nil)
	.Mach.Remove1(ssrpc.ServerStates.Start, nil)
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}

	// read env
	 := os.Getenv(dbg.EnvAmDbgAddr)
	 := am.EnvLogLevel("")

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	 := utils.NewRelsNetSrc(, nil)
	amhelpt.MachDebug(, , , , true)

	// init server and client
	, , ,  := (, , , nil, 0, false, nil, nil)

	// test
	 := am.S{sst.A, sst.C}
	.NetMach.Add(, nil)

	// assert
	assert.True(, .Mach.Is1(ssrpc.ServerStates.Ready), "Server ready")
	assert.True(, .Mach.Is1(ssrpc.ClientStates.Ready), "Client ready")

	assert.True(, .Mach.Not1(am.StateException), "No server errors")
	assert.True(, .Mach.Not1(am.StateException), "No client errors")

	assert.True(, .Is(), "NetworkMachine state set on the server")
	assert.True(, .NetMach.Is(),
		"NetworkMachine state set on the client")

	.Mach.Log("OK")
	.Mach.Log("OK")

	// shut down
	.Mach.Remove1(ssrpc.ClientStates.Start, nil)
	.Mach.Remove1(ssrpc.ServerStates.Start, nil)
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(false)

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	 := make(chan struct{})
	, , ,  := (, , nil, , 0, false, nil, nil)

	// test
	 := make(chan struct{})
	go func() {
		<-.NetMach.When1(sst.A, )
		close()
	}()
	 := am.S{sst.A, sst.C}
	.NetMach.Add(, nil)
	<-

	// mark log and counter
	.Mach.Log("OK")
	.Mach.Log("OK")
	close()

	// assert
	assert.Equal(, 0, int(.CallCount),
		"Server piggybacked clock on resp")
	assert.Equal(, 3, int(.CallCount),
		"Client called RemoteHello, RemoteHandshake, RemoteAdd")
	 := <-
	assert.LessOrEqual(, 1_300, int(),
		"Bytes transferred (both ways)")
	assert.GreaterOrEqual(, 20_000, int(),
		"Bytes transferred (both ways)")

	disposeTest(, , , true)
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(false)

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	 := make(chan struct{})
	, , ,  := (, , nil, , 0, false, nil, nil)

	// test
	 := make(chan struct{})
	go func() {
		<-.NetMach.When1(sst.D, )
		close()
	}()
	 := am.S{sst.A, sst.C}
	for  := 0;  < 500; ++ {
		.NetMach.Add(, nil)
	}
	.NetMach.Add1(sst.D, nil)
	<-

	// mark log and counter
	.Mach.Log("OK")
	.Mach.Log("OK")
	close()

	// assert
	assert.Equal(, 0, int(.CallCount),
		"Server piggybacked clock on resp")
	 := <-
	assert.LessOrEqual(, 20_000, int(),
		"Client called handshake (2) and A,C (500) and D(1)")
	assert.GreaterOrEqual(, 200_000, int(),
		"Client called handshake (2) and A,C (500) and D(1)")

	disposeTest(, , , true)
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	 := make(chan struct{})
	// disable clock pushes
	, , ,  := (, , nil, , 0, false, nil, nil)

	// test
	 := make(chan struct{})
	go func() {
		<-.NetMach.When1(sst.D, )
		close()
	}()
	 := am.S{sst.A, sst.C}
	for  := 0;  < 500; ++ {
		.NetMach.AddNS(, nil)
	}
	.NetMach.Add1NS(sst.D, nil)

	// wait for the network to settle, as Sync arrives before +D, this can be
	// flaky, and it's better to Add1(sstest.D, nil), but Sync() is being tested
	// here
	time.Sleep(1000 * time.Millisecond)

	// manual sync, should tick D
	.Sync()
	<-

	// mark log and counter
	.Mach.Log("OK")
	.Mach.Log("OK")
	close()

	// assert
	 := <-
	assert.LessOrEqual(, 10_000, int(),
		"Client called handshake (2) and A,C (500) and D(1)")
	assert.GreaterOrEqual(, 100_000, int(),
		"Client called handshake (2) and A,C (500) and D(1)")

	disposeTest(, , , true)
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	 := make(chan struct{})
	// disable clock optimization (instant pushes)
	 := 1 * time.Nanosecond
	, , ,  := (, , nil, , , false, nil, nil)

	// test
	 := make(chan struct{})
	go func() {
		<-.NetMach.When1(sst.D, )
		close()
	}()
	 := am.S{sst.A, sst.C}
	for  := 0;  < 500; ++ {
		.NetMach.Add(, nil)
	}
	.NetMach.Add1(sst.D, nil)
	<-

	// mark log and counter
	.Mach.Log("OK")
	.Mach.Log("OK")
	close()

	// assert
	 := <-
	assert.LessOrEqual(, 20_000, int(),
		"Bytes transferred (both ways)")
	// 549_751
	assert.GreaterOrEqual(, 200_000, int(),
		"Bytes transferred (both ways)")

	disposeTest(, , , true)
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}

	// amhelp.EnableDebugging(false)

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	 := make(chan struct{})

	// reuse the net src and add many rand states
	 := ssrpc.StateSourceSchema.Merge(sst.States)
	 := am.SAdd(ssrpc.StateSourceStates.Names(), sst.Names)
	 := 100
	for  := 0;  < ; ++ {
		 := fmt.Sprintf("State%d", )
		 = append(, )
		[] = am.State{}
	}
	,  := am.NewCommon(context.Background(), "ns-"+.Name(), ,
		, nil, nil, nil)
	if  != nil {
		.Fatal()
	}
	, , ,  := (, , , , 0, false, nil, nil)

	// test
	 := make(chan struct{})
	go func() {
		<-.NetMach.When1(sst.D, )
		close()
	}()
	.NetMach.Add1(sst.C, nil)
	for  := 5;  < -5; ++ {
		.NetMach.Remove1([-3], nil)
		.NetMach.Add1([], nil)
	}
	.NetMach.Add1(sst.D, nil)
	<-

	// mark log and counter
	.Mach.Log("OK")
	.Mach.Log("OK")
	close()

	// assert
	assert.Equal(, 184, int(.CallCount),
		"Client called handshake (2) and mutations (181)")
	 := <-
	assert.LessOrEqual(, 10_000, int(),
		"Bytes transferred (both ways)")
	assert.GreaterOrEqual(, 100_000, int(),
		"Bytes transferred (both ways)")

	disposeTest(, , , true)
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(false)

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	 := make(chan struct{})

	// reuse the worker and bump the clocks high
	 := utils.NewRelsNetSrc(, nil)
	 := .Clock(nil)
	[sst.A] = 1_000_000
	[sst.C] = 1_000_000
	am.TestMockClock(, )
	// disable clock optimization
	, , ,  := (, , , , 0, false, nil, nil)

	// test
	assert.GreaterOrEqual(, int(.Tick(sst.A)), 1_000_000,
		"Tick count should be greater or equal to 1M")
	 := make(chan struct{})
	go func() {
		<-.NetMach.When1(sst.D, )
		close()
	}()
	 := am.S{sst.A, sst.C}
	for  := 0;  < 500; ++ {
		.NetMach.Add(, nil)
		.NetMach.Remove(, nil)
	}
	.NetMach.Add1(sst.D, nil)
	<-

	// mark log and counter
	.Mach.Log("OK")
	.Mach.Log("OK")
	close()

	// assert
	// byte count should be the same as in TestAddManyInstantClock
	 := <-
	assert.LessOrEqual(, 48_000, int(),
		"Bytes transferred (both ways)")
	assert.GreaterOrEqual(, 350_000, int(),
		"Bytes transferred (both ways)")

	disposeTest(, , , true)
}

// TestRetryCall

func ( *testing.T,  NewTestFactory) {
	// TODO TestClockPush
	.Skip("test server-side mutations push their clock")
}

type TestRetryCallHandlers struct {
	blocked bool
}

func ( *TestRetryCallHandlers) ( *am.Event) {
	if .blocked {
		return
	}

	.Machine().Log("Blocking for 1s")
	time.Sleep(1 * time.Second)
	.blocked = true
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(false)

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	, , ,  := (, , nil, nil, 0, false, nil, nil)
	 := &TestRetryCallHandlers{}
	,  := .HandlersBind()
	require.NoError(, )

	// inject a fake error
	.TmpTestErr = fmt.Errorf("IGNORE MOCK ERR")
	 := .Mach.When1(ssrpc.ClientStates.RetryingCall, nil)
	.NetMach.Add1(sst.A, nil)
	amhelpt.WaitForAll(, "RetryingCall", , 2*time.Second, )

	// .TODO amtest
	assert.True(, .Mach.Is1(ssrpc.ServerStates.Ready), "Server ready")
	assert.True(, .Mach.Is1(ssrpc.ClientStates.Ready), "Client ready")

	.Mach.Log("Generic err retried")

	// extend the timeout to cause a network one (handler blocks for 1s)
	// TODO avoid races with the queue in iroh, remove in v0.20
	<-.WhenQueueEnds()
	time.Sleep(100*time.Millisecond)
	.HandlerTimeout = 5 * time.Second
	.CallTimeout = 500 * time.Millisecond
	 := sync.WaitGroup{}
	.Add(2)

	go func() {
		// this will block and retry
		.NetMach.Add1(sst.D, nil)
		.Done()
	}()
	go func() {
		<-.Mach.When1(ssrpc.ClientStates.RetryingCall, )
		.Mach.Log("Timeout err retried")
		.Done()
	}()

	.Wait()

	// TODO amtest asserts
	assert.True(, .Is1(sst.D), "NetworkMachine state set")
	assert.True(, .Mach.Is1(ssrpc.ServerStates.Ready), "Server ready")
	assert.True(, .Mach.Is1(ssrpc.ClientStates.Ready), "Client ready")
	assert.True(, .blocked, "Handlers should block")

	disposeTest(, , , false)
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// EnableDebuggingRpc(true)

	// config
	,  := context.WithCancel(context.Background())
	defer ()

	// test
	, , ,  := (, , nil, nil, 0, true, nil, nil)
	 := *.Listener.Load()
	 := .Addr()
	_ = .Close()
	.Addr = .String()

	go func() {
		// wait for client to reconnect and then start the server
		<-.Mach.WhenTime1(ssC.Connecting, 3, nil)
		.Start(nil)
	}()

	// client ready
	.Start(nil)
	amhelpt.WaitForAll(, "client-server Ready", , 5*time.Second,
		.Mach.When1(ssC.Ready, ),
		.Mach.When1(ssS.Ready, ))

	.NetMach.Add1(sst.A, nil)

	// assert
	amhelpt.AssertIs1(, .Mach, ssrpc.ClientStates.Ready)
	amhelpt.AssertIs1(, .Mach, ssrpc.ServerStates.Ready)

	amhelpt.AssertNot1(, .Mach, ssrpc.ClientStates.RetryingCall)
	amhelpt.AssertNot1(, .Mach, ssrpc.ClientStates.RetryingConn)

	.Mach.Log("Network err retried")

	disposeTest(, , , false)
}

// TestRetryErrNetworkTimeout

type TestRetryErrNetworkTimeoutHandlers struct {
	blocked     bool
	shouldBlock bool
}

func ( *TestRetryErrNetworkTimeoutHandlers) ( *am.Event) {
	if !.shouldBlock {
		return
	}
	.Machine().Log("Blocking for 1s")
	time.Sleep(1 * time.Second)
	.blocked = true
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(false)

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	, , ,  := (, , nil, nil, 0, false, nil, nil)
	 := &TestRetryErrNetworkTimeoutHandlers{
		shouldBlock: true,
	}
	,  := .HandlersBind()
	require.NoError(, )

	// test network timeout
	// extend the handler timeout (handler blocks for 1s)
	.HandlerTimeout = 5 * time.Second
	.CallTimeout = 500 * time.Millisecond
	 := sync.WaitGroup{}
	.Add(2)

	go func() {
		<-.Mach.When1(ssrpc.ClientStates.RetryingCall, )
		.Mach.Log("Timeout err retried")
		.Done()
	}()
	go func() {
		// this will block until connections restarts
		.NetMach.Add1(sst.D, nil)
		.Done()
	}()

	.Wait()

	// assert
	amhelpt.AssertIs1(, , sst.D)

	amhelpt.AssertIs1(, .Mach, ssrpc.ClientStates.Ready)
	amhelpt.AssertIs1(, .Mach, ssrpc.ServerStates.Ready)

	amhelpt.AssertNot1(, .Mach, ssrpc.ClientStates.RetryingCall)
	amhelpt.AssertNot1(, .Mach, ssrpc.ClientStates.RetryingConn)

	assert.True(, .blocked, "Handlers should block")

	disposeTest(, , , false)
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(true)

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	, , ,  := (, , nil, nil, 0, false, nil, nil)

	// close the listener and try a mutation
	 := *.Listener.Load()
	_ = .Close()
	time.Sleep(100 * time.Millisecond)
	.NetMach.Add1(sst.D, nil)

	// wait for D
	amhelpt.WaitForAll(, .Name(), , 2*time.Second,
		.NetMach.When1(sst.D, ))

	// assert
	amhelpt.AssertIs1(, .NetMach, sst.D)

	amhelpt.AssertIs1(, .Mach, ssrpc.ClientStates.Ready)
	amhelpt.AssertIs1(, .Mach, ssrpc.ServerStates.Ready)

	amhelpt.AssertNot1(, .Mach, ssrpc.ClientStates.RetryingCall)
	amhelpt.AssertNot1(, .Mach, ssrpc.ClientStates.RetryingConn)

	amhelpt.AssertTime(, .Mach,
		am.S{ssC.RetryingConn, ssC.HandshakeDone, ssC.Disconnected},
		am.Time{2, 3, 2})
	disposeTest(, , , false)
}

// TestPayload

type TestPayloadHandlers struct {
	srv *arpc.Server
}

// CState will trigger SendPayload
func ( *TestPayloadHandlers) ( *am.Event) {
	// TODO use v2 state def
	.Machine().EvRemove1(, sst.C, nil)
	 := am.ParseArgs[arpc.A](.Args)

	_ = .srv.SendPayload(context.Background(), , &arpc.MsgSrvPayload{
		Data: "Hello",
		Name: .Name,
	})
}

type TestPayloadConsumer struct {
	t         *testing.T
	delivered bool
}

func ( *TestPayloadConsumer) ( *am.Event) {
	.Machine().Remove1(ssCo.ServerPayload, nil)

	 := am.ParseArgs[arpc.AServerPayload](.Args)
	assert.Equal(.t, "TestPayload", .Name)
	assert.Equal(.t, "Hello", .Payload.Data.(string))

	.delivered = true
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(true)
	// EnableDebuggingRpc(true)

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	 := ssrpc.ConsumerStates

	// consumer
	 := &TestPayloadConsumer{t: }
	,  := am.NewCommon(, "TestPayloadConsumer",
		ssrpc.ConsumerSchema, .Names(), , nil, nil)
	if  != nil {
		.Fatal()
	}

	// source mach
	 := utils.NewNoRelsNetSrc(, nil, "")
	 := &TestPayloadHandlers{}
	_,  = .HandlersBind()
	if  != nil {
		.Fatal()
	}

	// init RPC
	, , ,  := (, , , nil, 0, false, &arpc.ClientOpts{
		Consumer: ,
	}, nil)
	.srv = 

	 := .When1(.ServerPayload, nil)
	// Consumer requests a payload from the remote worker
	.NetMach.Add1(sst.C, Pass(&arpc.A{
		Name: "TestPayload",
	}))
	// Consumer waits for WorkerDelivered
	 = amhelp.WaitForAll(, 2*time.Second, )

	// assert
	assert.NoError(, , "Timeout when waiting for the package")
	assert.True(, .delivered, "Consumer got the package")

	disposeTest(, , , true)
}

// TODO test gob errors (although not user-facing)

func ( *testing.T,  NewTestFactory) {
	.Skip("TODO")
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(false)

	// config
	,  := context.WithCancel(context.Background())
	defer ()

	// net source mach with non-zero clocks
	 := utils.NewNoRelsNetSrc(, nil, "")
	.Add1(sst.C, nil)
	.Remove1(sst.C, nil)

	// init RPC
	, , ,  := (, , , nil, 0, false, &arpc.ClientOpts{
		AllowedStates: am.S{sst.A, sst.B, sst.C},
		SkippedStates: am.S{sst.C},
	}, nil)

	// test
	.Add1(sst.A, nil)
	.Add(am.S{sst.B, sst.C}, nil)
	.Add1(sst.D, nil)

	// assert
	amhelpt.WaitForAll(, "TestPartial(A, B)", , time.Second,
		.NetMach.When(am.S{sst.A, sst.B}, nil))
	amhelpt.AssertIs(, .NetMach, am.S{sst.A, sst.B})

	// TODO schema change
	// TODO full sync

	// dispose
	disposeTest(, , , true)
	if amhelp.IsTelemetry() {
		time.Sleep(1 * time.Second)
	}
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(false)

	// config
	,  := context.WithCancel(context.Background())
	defer ()

	// net source mach
	 := utils.NewRelsNetSrc(, nil)

	// init RPC
	, , ,  := (, , , nil, 0, false, &arpc.ClientOpts{
		AllowedStates: am.S{sst.A},
	}, nil)

	// test (C will add A, and netmach will infer it from the schema)
	.Add1(sst.C, nil)

	// assert
	amhelpt.WaitForAll(, "TestPartial(A, C)", , time.Second,
		.NetMach.When(am.S{sst.A, sst.C}, nil))
	amhelpt.AssertIs(, .NetMach, am.S{sst.A, sst.C})

	// TODO schema change
	// TODO full sync

	// dispose
	disposeTest(, , , true)
	if amhelp.IsTelemetry() {
		time.Sleep(1 * time.Second)
	}
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(false)

	// config
	,  := context.WithCancel(context.Background())
	defer ()

	// net source mach
	 := utils.NewRelsNetSrc(, nil)

	// init RPC
	, , ,  := (, , , nil, 0, false, &arpc.ClientOpts{
		AllowedStates: am.S{sst.C},
		NoSchema:      true,
	}, nil)

	// test (C will add A, but netmach A wont be inferred)
	.Add1(sst.C, nil)

	// assert
	 := am.S{sst.C}
	amhelpt.WaitForAll(, "TestPartial(A, C)", , time.Second,
		.NetMach.When(, nil))
	amhelpt.AssertIs(, .NetMach, )

	// TODO schema change
	// TODO full sync

	// dispose
	disposeTest(, , , true)
	if amhelp.IsTelemetry() {
		time.Sleep(1 * time.Second)
	}
}

// TestSchemaFilteringSync

type TestSchemaFilteringSyncTracer struct {
	*am.TracerNoOp
	amount int
}

func ( *TestSchemaFilteringSyncTracer) (
	 am.Api,  *am.Mutation,
) {
	.amount++
}

func ( *testing.T,  NewTestFactory) {
	// TODO
	.Skip("mutation filtering not implemented yet")
	return

	// amhelp.EnableDebugging(false)

	// // config
	// ctx, cancel := context.WithCancel(context.Background())
	// defer cancel()
	// end := make(chan struct{})
	// counter, netSrc, s, c := NewTest(t, ctx, nil, end, time.Second, false,
	// 	nil, nil)
	//
	// qCount := &TestSchemaFilteringSyncTracer{}
	// require.NoError(t, netSrc.BindTracer(qCount))
	//
	// // test
	// // A req C
	// c.NetMach.Add1(sst.A, nil)
	// c.NetMach.Add(am.S{sst.C, sst.A}, nil)
	//
	// // mark log and counter
	// c.Mach.Log("OK")
	// s.Mach.Log("OK")
	// close(end)
	//
	// // assert
	// assert.Len(t, c.NetMach.schema, 1+len(sst.States)+
	//   len(ssrpc.NetSourceSchema), "schema len")
	// assert.Equal(t, 1, qCount.amount,
	// 	"one queued mutation, one filtered out")
	// bytesCount := <-counter
	// assert.LessOrEqual(t, 1_000, int(bytesCount))
	// assert.GreaterOrEqual(t, 2_000, int(bytesCount))
	// amhelpt.AssertIs(t, netSrc, am.S{sst.C, sst.A})
	//
	// disposeTest(t, c, s, true)
}

func ( *testing.T,  NewTestFactory) {
	.Skip("TODO")
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(false)

	// config
	,  := context.WithCancel(context.Background())
	defer ()

	// net source mach
	 := utils.NewRelsNetSrc(, nil)

	// init RPC
	, , ,  := (, , , nil, 0, false, &arpc.ClientOpts{
		NoSchema: true,
	}, nil)

	// test (C will add A, but netmach A wont be inferred)
	.Add1(sst.C, nil)

	// assert
	 := am.S{sst.A, sst.C}
	amhelpt.WaitForAll(, "TestNoSchema(A, B, C, D)", , time.Second,
		.NetMach.When(, nil))
	amhelpt.AssertIs(, .NetMach, )

	// TODO schema change
	// TODO full sync

	// dispose
	disposeTest(, , , true)
	if amhelp.IsTelemetry() {
		time.Sleep(1 * time.Second)
	}
}

// TestMutationsSync

type TestMutationsSyncTracer struct {
	*am.TracerNoOp
	amount int
}

func ( *TestMutationsSyncTracer) ( *am.Transition) {
	.amount++

	// TODO assert mut types, called states
}

func ( *testing.T,  NewTestFactory) {
	if os.Getenv(am.EnvAmTestDbgAddr) == "" {
		.Parallel()
	}
	// amhelp.EnableDebugging(false)

	// config
	,  := context.WithCancel(context.Background())
	defer ()
	 := make(chan struct{})
	, , ,  := (, , nil, , time.Second, false,
		&arpc.ClientOpts{
			SyncMutations: true,
		}, nil)

	 := &TestMutationsSyncTracer{TracerNoOp: &am.TracerNoOp{Id: .Name()}}
	,  := .NetMach.BindTracer()
	require.NoError(, )

	// test
	// add B and cause auto:A
	.Add1(sst.B, nil)
	.Add1(sst.D, nil)
	// let the source tracer finish
	time.Sleep(100 * time.Millisecond)
	// force push
	 := time.Nanosecond
	.PushInterval.Store(&)
	.PushClient()
	amhelpt.WaitForAll(, "mutations pushed", , time.Second,
		.NetMach.When1(sst.D, nil))

	// mark log and counter
	.Mach.Log("OK")
	.Mach.Log("OK")
	close()

	// assert
	assert.Equal(, 3, .amount,
		"3 mutations came from the server")
	 := <-
	assert.LessOrEqual(, 1_000, int())
	assert.GreaterOrEqual(, 2_000, int())

	disposeTest(, , , true)
}

func ( *testing.T,  NewTestFactory) {
	.Skip("TODO")

	// TODO assert mach tick
	// TODO schema change
}

// ///// ///// /////

// ///// UTILS

// ///// ///// /////

type NewTestFactory func(t *testing.T, ctx context.Context, netSrc *am.Machine,
	disposeMeter <-chan struct{}, pushInterval time.Duration, skipStart bool,
	clientOpts *arpc.ClientOpts, serverOpts *arpc.ServerOpts,
) (<-chan int64, *am.Machine, *arpc.Server, *arpc.Client)

func disposeTest( *testing.T,  *arpc.Client,  *arpc.Server,  bool) {
	if  {
		amhelpt.AssertNoErrEver(, .Mach)
		amhelpt.AssertNoErrEver(, .Mach)
	}
	if os.Getenv(dbg.EnvAmDbgAddr) != "" {
		time.Sleep(time.Second)
	}
	.Stop(context.TODO(), nil, true)
	<-.Mach.WhenDisposed()
	.Stop(nil, true)
}

// TCPMeter measures the traffic of a listener and forwards it to a
// destination. Results are sent to the [counter] channel. Useful for testing
// and benchmarking.
func (
	 net.Listener,  string,  chan<- int64,
	 <-chan struct{},
) {
	defer .Close()
	// fmt.Println("Listening on " + listenOn)

	// callFailsafe the destination
	,  := net.Dial("tcp4", )
	if  != nil {
		fmt.Println("Error connecting to destination:", .Error())
		return
	}
	defer .Close()

	// wait for the connection
	,  := .Accept()
	if  != nil {
		fmt.Println("Error accepting connection:", .Error())
		return
	}
	defer .Close()

	// forward data bidirectionally
	 := sync.WaitGroup{}
	.Add(2)
	 := atomic.Int64{}
	go func() {
		,  := io.Copy(, )
		.Add()
		.Done()
	}()
	go func() {
		,  := io.Copy(, )
		.Add()
		.Done()
	}()

	// wait for the test and forwarding to finish
	<-
	// fmt.Printf("Closing counter...\n")
	_ = .Close()
	_ = .Close()
	_ = .Close()
	.Wait()

	 := .Load()
	// fmt.Printf("Forwarded %d bytes\n", c)
	 <- 
}