runOn:
    -
        minServerVersion: "4.0"
        topology: ["replicaset"]
    -
        minServerVersion: "4.1.8"
        topology: ["sharded"]

database_name: &database_name "transaction-tests"
collection_name: &collection_name "test"

data: []
tests:
  - description: commit

    operations:
      - name: startTransaction
        object: session0
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 1
        result:
          insertedId: 1
      - name: commitTransaction
        object: session0
      # Again, to verify that txnNumber is incremented.
      - name: startTransaction
        object: session0
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 2
        result:
          insertedId: 2
      - name: commitTransaction
        object: session0

    expectations:
      - command_started_event:
          command:
            insert: *collection_name
            documents:
              - _id: 1
            ordered: true
            readConcern:
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction: true
            autocommit: false
            writeConcern:
          command_name: insert
          database_name: *database_name
      - command_started_event:
          command:
            commitTransaction: 1
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction:
            autocommit: false
            writeConcern:
          command_name: commitTransaction
          database_name: admin
      - command_started_event:
          command:
            insert: *collection_name
            documents:
              - _id: 2
            ordered: true
            readConcern:
              afterClusterTime: 42
            lsid: session0
            txnNumber:
              $numberLong: "2"
            startTransaction: true
            autocommit: false
            writeConcern:
          command_name: insert
          database_name: *database_name
      - command_started_event:
          command:
            commitTransaction: 1
            lsid: session0
            txnNumber:
              $numberLong: "2"
            startTransaction:
            autocommit: false
            writeConcern:
          command_name: commitTransaction
          database_name: admin

    outcome:
      collection:
        data:
          - _id: 1
          - _id: 2

  - description: rerun commit after empty transaction

    operations:
      - name: startTransaction
        object: session0
      - name: commitTransaction
        object: session0
      # Rerun the commit (which does not increment the txnNumber).
      - name: commitTransaction
        object: session0
      - name: startTransaction
        object: session0
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 1
        result:
          insertedId: 1
      - name: commitTransaction
        object: session0

    expectations:
      - command_started_event:
          command:
            insert: *collection_name
            documents:
              - _id: 1
            ordered: true
            readConcern:
            lsid: session0
            txnNumber:
              $numberLong: "2"
            startTransaction: true
            autocommit: false
            writeConcern:
          command_name: insert
          database_name: *database_name
      - command_started_event:
          command:
            commitTransaction: 1
            lsid: session0
            txnNumber:
              $numberLong: "2"
            startTransaction:
            autocommit: false
            writeConcern:
          command_name: commitTransaction
          database_name: admin

    outcome:
      collection:
        data:
          - _id: 1

  - description: multiple commits in a row

    operations:
      - name: startTransaction
        object: session0
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 1
        result:
          insertedId: 1
      - name: commitTransaction
        object: session0
      - name: commitTransaction
        object: session0
      - name: commitTransaction
        object: session0

    expectations:
      - command_started_event:
          command:
            insert: *collection_name
            documents:
              - _id: 1
            ordered: true
            readConcern:
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction: true
            autocommit: false
            writeConcern:
          command_name: insert
          database_name: *database_name
      - command_started_event:
          command:
            commitTransaction: 1
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction:
            autocommit: false
            writeConcern:
          command_name: commitTransaction
          database_name: admin
      - command_started_event:
          command:
            commitTransaction: 1
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction:
            autocommit: false
            # commitTransaction applies w:majority on retries
            writeConcern: { w: majority, wtimeout: 10000 }
          command_name: commitTransaction
          database_name: admin
      - command_started_event:
          command:
            commitTransaction: 1
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction:
            autocommit: false
            writeConcern: { w: majority, wtimeout: 10000 }
          command_name: commitTransaction
          database_name: admin

    outcome:
      collection:
        data:
          - _id: 1

  - description: write concern error on commit
    operations:
      - name: startTransaction
        object: session0
        arguments:
          options:
            writeConcern:
              w: 10
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 1
        result:
          insertedId: 1
      - name: commitTransaction
        object: session0
        result:
          # {
          #   'ok': 1.0,
          #   'writeConcernError': {
          #     'code': 100,
          #     'codeName': 'UnsatisfiableWriteConcern',
          #     'errmsg': 'Not enough data-bearing nodes'
          #   }
          # }
          errorLabelsOmit: ["TransientTransactionError", "UnknownTransactionCommitResult"]

    outcome:
      collection:
        data:
          - _id: 1

  - description: commit without start

    operations:
      - name: commitTransaction
        object: session0
        result:
          errorContains: no transaction started

    expectations: []

    outcome:
      collection:
        data: []

  - description: commit after no-op abort

    operations:
      - name: startTransaction
        object: session0
      - name: abortTransaction
        object: session0
      - name: commitTransaction
        object: session0
        result:
          errorContains: Cannot call commitTransaction after calling abortTransaction

    expectations: []

    outcome:
      collection:
        data: []

  - description: commit after abort

    operations:
      - name: startTransaction
        object: session0
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 1
        result:
          insertedId: 1
      - name: abortTransaction
        object: session0
      - name: commitTransaction
        object: session0
        result:
          errorContains: Cannot call commitTransaction after calling abortTransaction

    expectations:
      - command_started_event:
          command:
            insert: *collection_name
            documents:
              - _id: 1
            ordered: true
            readConcern:
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction: true
            autocommit: false
            writeConcern:
          command_name: insert
          database_name: *database_name
      - command_started_event:
          command:
            abortTransaction: 1
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction:
            autocommit: false
            writeConcern:
          command_name: abortTransaction
          database_name: admin

  - description: multiple commits after empty transaction

    operations:
      - name: startTransaction
        object: session0
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 1
        result:
          insertedId: 1
      - name: abortTransaction
        object: session0
      # Increments txnNumber.
      - name: startTransaction
        object: session0
      # These commits aren't sent to server, transaction is empty.
      - name: commitTransaction
        object: session0
      - name: commitTransaction
        object: session0
      # Verify that previous, empty transaction incremented txnNumber.
      - name: startTransaction
        object: session0
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 1
        result:
          insertedId: 1
      - name: abortTransaction
        object: session0

    expectations:
      - command_started_event:
          command:
            insert: *collection_name
            documents:
              - _id: 1
            ordered: true
            readConcern:
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction: true
            autocommit: false
            writeConcern:
          command_name: insert
          database_name: *database_name
      - command_started_event:
          command:
            abortTransaction: 1
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction:
            autocommit: false
            writeConcern:
          command_name: abortTransaction
          database_name: admin
      - command_started_event:
          command:
            insert: *collection_name
            documents:
              - _id: 1
            ordered: true
            readConcern:
              afterClusterTime: 42
            lsid: session0
            # txnNumber 2 was skipped.
            txnNumber:
              $numberLong: "3"
            startTransaction: true
            autocommit: false
            writeConcern:
          command_name: insert
          database_name: *database_name
      - command_started_event:
          command:
            abortTransaction: 1
            lsid: session0
            txnNumber:
              $numberLong: "3"
            startTransaction:
            autocommit: false
            writeConcern:
          command_name: abortTransaction
          database_name: admin

    outcome:
      collection:
        data: []

  - description: reset session state commit
    clientOptions:
      retryWrites: false
    operations:
      - name: startTransaction
        object: session0
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 1
        result:
          insertedId: 1
      - name: commitTransaction
        object: session0
      # Running any operation after an ended transaction resets the session
      # state to "no transaction".
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 2
        result:
          insertedId: 2
      # Calling commit again should error instead of re-running the commit.
      - name: commitTransaction
        object: session0
        result:
          errorContains: no transaction started

    expectations:
      - command_started_event:
          command:
            insert: *collection_name
            documents:
              - _id: 1
            ordered: true
            readConcern:
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction: true
            autocommit: false
            writeConcern:
          command_name: insert
          database_name: *database_name
      - command_started_event:
          command:
            commitTransaction: 1
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction:
            autocommit: false
            writeConcern:
          command_name: commitTransaction
          database_name: admin
      - command_started_event:
          command:
            insert: *collection_name
            documents:
              - _id: 2
            ordered: true
            readConcern:
            lsid: session0
            txnNumber:
            startTransaction:
            autocommit:
          command_name: insert
          database_name: *database_name

    outcome:
      collection:
        data:
          - _id: 1
          - _id: 2

  - description: reset session state abort
    clientOptions:
      retryWrites: false
    operations:
      - name: startTransaction
        object: session0
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 1
        result:
          insertedId: 1
      - name: abortTransaction
        object: session0
      # Running any operation after an ended transaction resets the session
      # state to "no transaction".
      - name: insertOne
        object: collection
        arguments:
          session: session0
          document:
            _id: 2
        result:
          insertedId: 2
      # Calling abort should error with "no transaction started" instead of
      # "cannot call abortTransaction twice".
      - name: abortTransaction
        object: session0
        result:
          errorContains: no transaction started

    expectations:
      - command_started_event:
          command:
            insert: *collection_name
            documents:
              - _id: 1
            ordered: true
            readConcern:
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction: true
            autocommit: false
            writeConcern:
          command_name: insert
          database_name: *database_name
      - command_started_event:
          command:
            abortTransaction: 1
            lsid: session0
            txnNumber:
              $numberLong: "1"
            startTransaction:
            autocommit: false
            writeConcern:
          command_name: abortTransaction
          database_name: admin
      - command_started_event:
          command:
            insert: *collection_name
            documents:
              - _id: 2
            ordered: true
            readConcern:
            lsid: session0
            txnNumber:
            startTransaction:
            autocommit:
          command_name: insert
          database_name: *database_name

    outcome:
      collection:
        data:
          - _id: 2
