it('where all things have associated principals', function(done) {
    var self = this;
    var testName = self.test.fullTitle();

    context.logger.info(testName);

    var listThingsResponse = {
      "things": [
        {
          "attributes": {
            "attr": "val"
          },
          "thingName": "test"
        },
        {
          "attributes": {},
          "thingName": "led"
        }
      ]
    };

    var iot = new AWS.Iot();

    var listThingsStub = sinon.stub(iot, 'listThings');
    listThingsStub.yields(null, listThingsResponse);

    var listThingPrincipals = sinon.stub(iot, 'listThingPrincipals');
    for (var i = 0; i < listThingsResponse.things.length; i++) {
      var thing = listThingsResponse.things[i];

      listThingPrincipals
        .withArgs({
            thingName: thing.thingName
          })
        .yields(null, {
            "principals": [
              String(randomString(64, 'hex'))
            ]
          });
    }

    var message = {};

    subject(message, context, iot)
      .finally(function() {
          listThingsStub.restore();
        })
      .should.eventually.be.fulfilled
      .and.to.deep.equal(transformListThingsResponse(listThingsResponse))
      .and.notify(done);
  });
    it('for listThings', function(done) {
      var self = this;
      var testName = self.test.fullTitle();

      context.logger.info(testName);

      var thingId = 'listThingsPermission';

      var detail = {
        cause: {
          message: 'User: arn:aws:sts::012345678901:assumed-role/lambdaFunction is not authorized to perform: iot:ListThings on resource: arn:aws:iot:us-east-1:012345678901:thing/' + thingId,
          code: 'AccessDeniedException',
          statusCode: 403,
          retryable: false,
          retryDelay: 30
        },
        code: 'AccessDeniedException',
        statusCode: 403,
        retryable: false,
        retryDelay: 30
      };

      var iot = new AWS.Iot();

      var listThingsStub = sinon.stub(iot, 'listThings');
      listThingsStub.throws(AWS.util.error(new Error(), detail));

      var message = {
      };

      subject(message, context, iot)
        .catch(function(err) {
          context.logger.error({ error: err }, testName);

          throw err;
        })
        .finally(function() {
          listThingsStub.restore();
        })
        .should.be.rejectedWith(/^AccessDeniedError/)
        .and.notify(done);
    });
  it('where some things have no associated principals', function(done) {
    var self = this;
    var testName = self.test.fullTitle();

    context.logger.info(testName);

    var listThingsResponse = {
      "things": [
        {
          "attributes": {
            "attr": "val"
          },
          "thingName": "test"
        },
        {
          "attributes": {},
          "thingName": "led"
        }
      ]
    };

    var expectedThing = deepcopy(listThingsResponse.things[1]);

    var iot = new AWS.Iot();

    var listThingsStub = sinon.stub(iot, 'listThings');
    listThingsStub.yields(null, listThingsResponse);

    var listThingPrincipals = sinon.stub(iot, 'listThingPrincipals');
    listThingPrincipals
      .withArgs({
          thingName: listThingsResponse.things[0].thingName
        })
      .yields(null, {
          "principals": []
        });
    listThingPrincipals
      .withArgs({
          thingName: expectedThing.thingName
        })
      .yields(null, {
        "principals": [
          String(randomString(64, 'hex'))
        ]
      });

    var message = {};

    subject(message, context, iot)
      .then(function(result) {
        context.logger.info({ result: result }, testName);

        return result;
      })
      .finally(function() {
        listThingsStub.restore();
      })
      .should.eventually.be.fulfilled
      .and.to.deep.equal(transformListThingsResponse({
          "things": [
            expectedThing
          ]
        }))
      .and.notify(done);
  });