You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: txn_coord_sender_server_test.go
    path of file: ./repos/cockroach/pkg/kv/kvclient/kvcoord
    
    the code of the file until where you have to start completion: // Copyright 2018 The Cockroach Authors.
//
// Use of this software is governed by the Business S
    
func TestHeartbeatFindsOutAboutAbortedTransaction(t *testing.T) {
	defer leaktest.AfterTest(t)()
	defer log.Scope(t).Close(t)

	var cleanupSeen int64
	key := roachpb.Key("a")
	key2 := roachpb.Key("b")
	s, _, origDB := serverutils.StartServer(t, base.TestServerArgs{
		Knobs: base.TestingKnobs{
			Store: &kvserver.StoreTestingKnobs{
				TestingProposalFilter: func(args kvserverbase.ProposalFilterArgs) *kvpb.Error {
					// We'll eventually expect to see an EndTxn(commit=false)
					// with the right intents.
					if args.Req.IsSingleEndTxnRequest() {
						et := args.Req.Requests[0].GetInner().(*kvpb.EndTxnRequest)
						if !et.Commit && et.Key.Equal(key) &&
							reflect.DeepEqual(et.LockSpans, []roachpb.Span{{Key: key}, {Key: key2}}) {
							atomic.StoreInt64(&cleanupSeen, 1)
						}
					}
					return nil
				},
			},
		},
	})
	ctx := context.Background()
	defer s.Stopper().Stop(ctx)

	push := func(ctx context.Context, key roachpb.Key) error {
		// Conflicting transaction that pushes the above transaction.
		conflictTxn := kv.NewTxn(ctx, origDB, 0 /* gatewayNodeID */)
		// We need to explicitly set a high priority for the push to happen.
		if err := conflictTxn.SetUserPriority(roachpb.MaxUserPriority); err != nil {
			return err
		}
		// Push through a Put, as opposed to a Get, so that the pushee gets aborted.
		if err := conflictTxn.Put(ctx, key, "pusher was here"); err != nil {
			return err
		}
		return conflictTxn.Commit(ctx)
	}

	// Make a db with a short heartbeat interval.
	ambient := s.AmbientCtx()
	tsf := kvcoord.NewTxnCoordSenderFactory(
		kvcoord.TxnCoordSenderFactoryConfig{
			AmbientCtx: ambient,
			// Short heartbeat interval.
			HeartbeatInterval: time.Millisecond,
			Settings:          s.ClusterSettings(),
			Clock:             s.Clock(),
			Stopper:           s.Stopper(),
		},
		s.DistSenderI().(*kvcoord.DistSender),
	)
	db := kv.NewDB(ambient, tsf, s.Clock(), s.Stopper())
	txn := kv.NewTxn(ctx, db, 0 /* gatewayNodeID */)
	if err := txn.Put(ctx, key, "val"); err != nil {
		t.Fatal(err)
	}
	if err := txn.Put(ctx, key2, "val"); err != nil {
		t.Fatal(err)
	}

	if err := push(ctx, key); err != nil {
		t.Fatal(err)
	}

	// Now wait until the heartbeat loop notices that the transaction is aborted.
	testutils.SucceedsSoon(t, func() error {
		if txn.Sender().(*kvcoord.TxnCoordSender).IsTracking() {
			return fmt.Errorf("txn heartbeat loop running")
		}
		return nil
	})

	// Check that an EndTxn(commit=false) with the right intents has been sent.
	testutils.SucceedsSoon(t, func() error {
		if atomic.LoadInt64(&cleanupSeen) == 0 {
			return fmt.Errorf("no cleanup sent yet")
		}
		return nil
	})

	// Check that further sends through the aborted txn are rejected. The
	// TxnCoordSender is supposed to synthesize a TransactionAbortedError.
	if err := txn.Commit(ctx); !testutils.IsError(
		err, "TransactionRetryWithProtoRefreshError: TransactionAbortedError",
	) {
		t.Fatalf("expected aborted error, got: %s", err)
	}
}