text:
require('../helpers')(global);
var chai = require('chai'),
  should = chai.should(),
  validator = require('validator'),
  _ = require('underscore'),
  express = require('express'),
  app = require('../../middleware/appConfigure')(express()),
  sinon = require("sinon"),
  sinonChai = require("sinon-chai"),
  request = require('supertest'),
  proxyquire = require('proxyquire'),
  eventStoreClient = {
    streams: {
      post: sinon.stub()
    },
    projection: {
      getState: sinon.stub()
    }
  },
  hypermediaResponseStub = {
    inboxes: {
      POST: sinon.stub(),
      uuid: {
        GET: sinon.stub(),
        commits: {
          POST: sinon.stub()
        }
      }
    },

  },
  inboxAdded = {
    validate: sinon.stub(),
    create: sinon.stub()
  },
  sanitizer = {
    sanitize: sinon.stub()
  },
  translator = {
    translatePush: sinon.stub()
  },
  validator = {
    isUUID: sinon.stub()
  },
  urls = {
    href: sinon.stub()
  },
  controller = proxyquire('../../api/inboxesController', {
    './hypermediaResponse': hypermediaResponseStub,
    './sanitizer': sanitizer,
    './events/inboxAdded': inboxAdded,
    './helpers/eventStoreClient': eventStoreClient,
    './translators/githubTranslator': translator,
    'validator': validator,
    './urls': urls
  });

chai.use(sinonChai);
chai.config.includeStack = true;

controller.init(app);

var getInbox = function(path, shouldBehaveThusly) {
  request(app)
    .get(path)
    .end(shouldBehaveThusly);
}

var postInboxCreate = function(payload, shouldBehaveThusly, contentType) {
  request(app)
    .post('/api/inboxes')
    .send(JSON.stringify(payload))
    .type(contentType || 'application/json')
    .end(shouldBehaveThusly);
};

var postInbox = function(payload, shouldBehaveThusly, contentType, inboxUuid, xGithubEventValue) {
  var postUrl = '/api/inboxes/' + (inboxUuid || 'c347948f-e1d0-4cd7-9341-f0f6ef5289bf') + '/commits';
  request(app)
    .post(postUrl)
    .set('x-github-event', xGithubEventValue || 'push')
    .send(JSON.stringify(payload))
    .type(contentType || 'application/json')
    .end(shouldBehaveThusly);
};

describe('inboxesController', function() {

  describe('when creating an inbox (/api/inboxes)', function() {

    var digestId = 'e9be4a71-f6ca-4f02-b431-d74489dee5d0';
    var payload = {
      name: 'His name was Robert Paulson',
      digestId: digestId,
      family: 'GitHub'
    };

    describe('with an unsupported or missing Content-Type header', function() {
      var payload = {};
      it('should reject request and return a 415 status code (Unsupported Media Type).', function(done) {
        postInboxCreate(payload, function(err, res) {
          res.statusCode.should.equal(415);
          done();
        }, 'application/jackson');
      });

      it('it should reject the request and explain that only application/json is accepted when sending unsupported Content-Type.', function(done) {
        postInboxCreate(payload, function(err, res) {
          res.text.should.equal('When creating an inbox, you must send a Content-Type: application/json header.');
          done();
        }, 'application/jackson');
      });

      it('it should reject the request and explain that only application/json is accepted when not setting Content-Type', function(done) {
        var postInboxCreateNoContentType = function(payload, shouldBehaveThusly, contentType) {
          request(app)
            .post('/api/inboxes')
            .send(JSON.stringify(payload))
            .end(shouldBehaveThusly);
        };

        postInboxCreateNoContentType(payload, function(err, res) {
          res.text.should.equal('When creating an inbox, you must send a Content-Type: application/json header.');
          done();
        });
      })
    });

    describe('with a valid payload', function() {
      var inboxId = '53d8c6ac-37f4-453f-b252-cb2d93c18fa7';

      var protocol = 'http';
      var host = 'localhost';

      var hypermediaResponse = {
        "_links": {
          "self": {
            "href": protocol + "://" + host + "/api/inboxes/" + inboxId
          }
        }
      };

      var inboxAddedEvent = {
        eventType: 'InboxAdded',
        eventId: 'a4cfd6f3-3cb4-4504-ae52-cc8d9fcb8818',
        data: {
          digestId: digestId,
          inboxId: inboxId,
          family: 'GitHub',
          name: 'some name',
          url: 'http://www.someURL.com'
        }
      };

      before(function() {
        sanitizer.sanitize.returns([]);
        inboxAdded.validate.returns([]);
        inboxAdded.create.returns(inboxAddedEvent);
        hypermediaResponseStub.inboxes.POST.returns(hypermediaResponse);
      })

      beforeEach(function() {
        eventStoreClient.streams.post.callsArgWith(1, null, "unused response");

        eventStoreClient.projection.getState.callsArgWith(1, null, {
          body: JSON.stringify({
            digestId: digestId
          }),
          statusCode: 200
        });

        urls.href = sinon.stub();

        urls.href.returns(function() {});
      });

      it('it should have a response Content-Type of hal+json', function(done) {
        postInboxCreate(payload, function(err, res) {
          res.get('Content-Type').should.equal('application/hal+json; charset=utf-8');
          done();
        });
      });

      it('it should use proper arguments when creating hypermedia.', function(done) {
        postInboxCreate(payload, function(err, res) {
          hypermediaResponseStub.inboxes.POST.should.have.been.calledWith(sinon.match.func, inboxAddedEvent.data.inboxId);
          done();
        });
      });

      it('it should call urls.href once.', function(done) {
        postInboxCreate(payload, function(err, res) {
          urls.href.should.have.been.calledOnce;
          done();
        });
      });

      it('it should have a response code of 201 created', function(done) {
        postInboxCreate(payload, function(err, res) {
          res.status.should.equal(201);
          done();
        });
      });

      it('it should set the Location response header to the newly created digest', function(done) {
        postInboxCreate(payload, function(err, res) {
          res.get('Location').should.equal(hypermediaResponse._links.self.href);
          done();
        });
      });

      describe('but an unsupported Content-Type header', function() {
        it('it should reject the request and explain that only application/json is accepted.', function(done) {
          postInboxCreate(payload, function(err, res) {
            res.text.should.equal('When creating an inbox, you must send a Content-Type: application/json header.');
            done();
          }, 'application/jackson');
        });

        it('should reject request and return a 415 status code (Unsupported Media Type).', function(done) {
          postInboxCreate(payload, function(err, res) {
            res.statusCode.should.equal(415);
            done();
          }, 'application/jackson');
        });
      });

      describe('but with illegal content in the name field', function() {
        before(function() {
          sanitizer.sanitize.returns(['error']);
        })

        it('should call sanitizer.sanitize with correct parameters.', function(done) {
          postInboxCreate(payload, function() {
            sanitizer.sanitize.should.have.been.calledWith('inbox', payload, ['name']);
            done();
          });
        });
      });

      describe('but with a schema violation', function() {
        before(function() {
          sanitizer.sanitize.returns([]);
          inboxAdded.validate.returns(['error']);
        })

        it('should validate the payload against an inboxAdded schema', function(done) {
          postInboxCreate(payload, function() {
            inboxAdded.validate.should.have.been.calledWith(payload);
            done();
          });
        })
      });

      describe('and failures occur when posting to eventstore', function() {

        beforeEach(function() {
          eventStoreClient.streams.post.callsArgWith(1, 'Houston, we have a problem', null);
          sanitizer.sanitize.returns([]);
          inboxAdded.validate.returns([]);
        });

        it('it should send back an appropriate error response', function(done) {
          postInboxCreate(payload, function(err, res) {
            shouldBeGenericError(res);
            done();
          });
        });
      });

      describe('and an HTTP timeout occurs with a status of 408 (Request Timeout) when posting to eventstore', function() {
        var response;

        before(function() {
          eventStoreClient.streams.post.callsArgWith(1, null, {
            statusCode: 408
          });

          postInboxCreate(payload, function(err, res) {
            response = res;
          });
        })

        it('it should report that there was an internal problem', function(done) {
          shouldBeGenericError(response);
          done();
        });

        it('it should report a status code of 500 (Internal Server Error)', function(done) {
          response.status.should.equal(500);
          done();
        });
      });
    });

    describe('for a non-existant digestId', function() {

      var nonExistantDigestId = 'dbb47eec-514c-441d-bb15-b7d6d3d2153c';
      var payload = {
        digestId: nonExistantDigestId
      }

      before(function() {
        eventStoreClient.projection.getState.callsArgWith(1, null, {
          body: {},
          statusCode: 200
        });
        sanitizer.sanitize.returns([]);
        inboxAdded.validate.returns([]);
      })

      it('should reject request and return a 404 status code.', function(done) {
        postInboxCreate(payload, function(err, res) {
          res.statusCode.should.equal(404);
          done();
        });
      });

      it('should call eventStore.projection.getState with the appropriate parameters', function(done) {
        postInboxCreate(payload, function(err, res) {
          eventStoreClient.projection.getState.should.have.been.calledWith({
            name: sinon.match.any,
            partition: 'digest-' + nonExistantDigestId
          }, sinon.match.any);
          done();
        });
      });
    });

    describe('and an error occurs from eventstore when looking up a digest', function() {
      var digestId = 'dbb47eec-514c-441d-bb15-b7d6d3d2153c';
      var payload = {
        digestId: digestId
      }

      before(function() {
        eventStoreClient.projection.getState.callsArgWith(1, 'error', {});
        sanitizer.sanitize.returns([]);
        inboxAdded.validate.returns([]);
      })

      it('a status code of 500 Internal Error should be reported.', function(done) {
        postInboxCreate(payload, function(err, res) {
          res.statusCode.should.equal(500);
          done();
        });
      });

      it('it should send an appropriate error message.', function(done) {
        postInboxCreate(payload, function(err, res) {
          shouldBeGenericError(res);
          done();
        });
      });

      it('it should have an application/json content-type', function(done) {
        postInboxCreate(payload, function(err, res) {
          res.get('Content-Type').should.equal('application/json; charset=utf-8');
          done();
        });
      });
    });

    describe('and an HTTP status code of 408 (Request Timeout) is received when looking up a digest', function() {
      var response;

      before(function() {
        eventStoreClient.projection.getState.callsArgWith(1, null, {
          statusCode: 408
        });

        postInboxCreate(payload, function(err, res) {
          response = res;
        });
      })

      it('it should report that there is a problem communicating with eventstore', function(done) {
        shouldBeGenericError(response);
        done();
      });

      it('it should report a status code of 500 (Internal Server Error)', function(done) {
        response.status.should.equal(500);
        done();
      });
    });

    describe('and santize reports an error', function() {
      before(function() {
        sanitizer.sanitize.returns('Houston, we have a problem');
      });

      it('an appropriate error message is sent', function(done) {
        postInboxCreate(payload, function(err, res) {
          JSON.parse(res.text).errors.should.equal('Houston, we have a problem');
          done();
        });
      });

      it('an appropriate response code is sent', function(done) {
        postInboxCreate(payload, function(err, res) {
          res.statusCode.should.equal(400);
          done();
        });
      });
    });

    describe('and validate reports an error', function() {
      before(function() {
        sanitizer.sanitize.returns([]);
        inboxAdded.validate.returns('Houston, we have a problem.');
      });

      it('an appropriate error message is sent', function(done) {
        postInboxCreate(payload, function(err, res) {
          JSON.parse(res.text).errors.should.equal('Houston, we have a problem.');
          done();
        });
      });
    });
  });

  describe('when posting to an inbox (/api/inboxes/:uuid/commits)', function() {
    var inboxId = 'c347948f-e1d0-4cd7-9341-f0f6ef5289bf';
    var digestId = 'e9be4a71-f6ca-4f02-b431-d74489dee5d0';
    var protocol = 'http';
    var host = 'localhost';

    var inboxPayload = {};

    var translatorEvent = {
      eventId: 'b0d65208-2afc-43f0-8926-6b20026ab1eb',
      eventType: 'GitHubCommitReceived',
      data: {},
      metadata: {
        digestId: digestId
      }
    };

    describe('with a corrupted push event', function() {
      var ex = new TypeError('Cannot read property \'id\' of undefined');
      var gitHubCommitMalformedError = new translator.GitHubCommitMalformedError(ex);
      before(function() {
        translator.translatePush.throws(gitHubCommitMalformedError);
        eventStoreClient.projection.getState.callsArgWith(1, null, {
          body: JSON.stringify({
            digestId: digestId
          }),
          statusCode: 200
        });
        validator.isUUID.returns(true);
      });
      it('should reject the request and explain with a proper message', function(done) {
        var errors = {
          errors: ['CommitStream was unable to process this request. Encountered the following exception while attempting to process the push event message:\n\nTypeError: Cannot read property \'id\' of undefined']
        };
        postInbox(inboxPayload, function(err, res) {
          res.status.should.equal(400);
          res.text.should.equal(JSON.stringify(errors));
          done();
        });

      });
    });

    describe('with a push event that fails for an unexpected reason', function() {
      var ex = new Error('Other unexpected error');
      before(function() {
        translator.translatePush.throws(ex);
        eventStoreClient.projection.getState.callsArgWith(1, null, {
          body: JSON.stringify({
            digestId: digestId
          }),
          statusCode: 200
        });
        validator.isUUID.returns(true);
      });
      it('should reject the request and explain with a proper message', function(done) {
        var errors = {
          errors: ['CommitStream was unable to process this request. Encountered an unexpected exception.']
        };
        postInbox(inboxPayload, function(err, res) {
          res.status.should.equal(500);
          res.text.should.equal(JSON.stringify(errors));
          done();
        });

      });
    });

    describe('with a valid inboxId', function() {
      var hypermedia = {
        "_links": {
          "self": {
            "href": protocol + "://" + host + "/api/inboxes/" + inboxId
          },
          "digest-parent": {
            "href": protocol + "://" + host + "/api/digests/" + digestId
          },
          "query-digest": {
            "href": protocol + "://" + host + "/api/query?digestId=" + digestId + "&workitem=all"
          }
        },
        "message": "Your push event has been queued to be added to CommitStream."
      };

      before(function() {
        translator.translatePush = sinon.stub();
        translator.translatePush.returns(translatorEvent);
        translatorEvent = JSON.stringify(translatorEvent);
        validator.isUUID.returns(true);
        eventStoreClient.streams.post.callsArgWith(1, null, 'Everything is OK.')

        eventStoreClient.projection.getState.callsArgWith(1, null, {
          body: JSON.stringify({
            digestId: digestId
          }),
          statusCode: 200
        });

        hypermediaResponseStub.inboxes.uuid.commits.POST.returns(hypermedia)

      });

      beforeEach(function() {
        urls.href = sinon.stub();
        urls.href.returns(function() {});
      });

      it('it should reject the request and explain that only application/json is accepted when sending unsupported Content-Type.', function(done) {
        postInbox(inboxPayload, function(err, res) {
          res.text.should.equal('When posting to an inbox, you must send a Content-Type: application/json header.');
          done();
        }, 'application/jackson');
      });

      it('it should send back an appropriate created status code of 201', function(done) {
        postInbox(inboxPayload, function(err, res) {
          res.status.should.equal(201);
          done();
        });
      });

      it('it should have a location header set with a place to query for that commit added.', function(done) {
        postInbox(inboxPayload, function(err, res) {
          res.get('Location').should.equal('http://localhost/api/query?digestId=' + digestId + '&workitem=all');
          done();
        });
      });

      it('it should call eventStoreClient to get the Parent DigestId for this inbox', function(done) {
        postInbox(inboxPayload, function(err, res) {
          eventStoreClient.projection.getState.should.have.been.calledWith({
            name: sinon.match.any,
            partition: 'inbox-' + inboxId
          }, sinon.match.any);
          done();
        }, null, inboxId);
      });

      it('should validate the inbox id with the correct params', function(done) {
        postInbox(inboxPayload, function(err, res) {
          validator.isUUID.should.have.been.calledWith(inboxId);
          done();
        }, null, inboxId);
      });

      it('it should call the translator with the correct params', function(done) {
        postInbox(inboxPayload, function(err, res) {
          translator.translatePush.should.have.been.calledWith(inboxPayload, digestId)
          done();
        }, null, inboxId);
      });

      it('it should post to the eventStoreClient with the correct params', function(done) {
        postInbox(inboxPayload, function(err, res) {
          var eventStoreClientParam1 = {
            name: 'inboxCommits-' + inboxId,
            events: translatorEvent
          };
          eventStoreClient.streams.post.should.have.been.calledWith(eventStoreClientParam1, sinon.match.any);
          done();
        });
      });

      it('asks for the appropriate hypermedia response', function(done) {
        var hypermediaParams = {
          inboxId: inboxId,
          digestId: digestId
        };

        postInbox(inboxPayload, function(err, res) {
          hypermediaResponseStub.inboxes.uuid.commits.POST.should.have.been.calledWith(sinon.match.func, hypermediaParams);
          done();
        });
      });

      it('it should call urls.href once.', function(done) {
        postInbox(inboxPayload, function(err, res) {
          urls.href.should.have.been.calledOnce;
          done();
        });
      });

      it('it should have an appropriate response message', function(done) {
        postInbox(inboxPayload, function(err, res) {
          var parsedResponse = JSON.parse(res.text);
          parsedResponse.message.should.equal('Your push event has been queued to be added to CommitStream.');
          done();
        }, null, inboxId);
      })

      it('it should have a response Content-Type of hal+json', function(done) {

        postInbox(inboxPayload, function(err, res) {
          res.get('Content-Type').should.equal('application/hal+json; charset=utf-8');
          done();
        }, null, inboxId);
      });

      describe('but with an unrecognized x-github-event-header', function() {
        var eventType = 'unrecognizedEventType';
        it('it should reply with a meaningful message.', function(done) {
          postInbox(inboxPayload, function(err, res) {
            JSON.parse(res.text).message.should.equal('Unknown event type for x-github-event header : ' + eventType);
            done();
          }, null, inboxId, eventType);
        });

        it('it should have a response Content-Type of application/json', function(done) {
          postInbox(inboxPayload, function(err, res) {
            res.get('Content-Type').should.equal('application/json; charset=utf-8');
            done();
          }, null, inboxId, eventType);
        });

        it('it should have a response code of 400 (Bad Request)', function(done) {
          postInbox(inboxPayload, function(err, res) {
            res.status.should.equal(400);
            done();
          }, null, inboxId, eventType);
        });
      })

      describe('but with an x-github-event-header of ping', function() {
        it('it should reply with Pong', function(done) {
          postInbox(inboxPayload, function(err, res) {
            JSON.parse(res.text).message.should.equal('Pong.');
            done();
          }, null, inboxId, 'ping');
        })

        it('it should have a response Content-Type of application/json', function(done) {
          postInbox(inboxPayload, function(err, res) {
            res.get('Content-Type').should.equal('application/json; charset=utf-8');
            done();
          }, null, inboxId, 'ping');
        });

        it('it should have a response code of 200 (OK)', function(done) {
          postInbox(inboxPayload, function(err, res) {
            res.status.should.equal(200);
            done();
          }, null, inboxId, 'ping');
        });
      });

      describe('but without the x-github-event header', function() {
        var postInboxWithoutXGithubEvent = function(shouldBehaveThusly) {
          var inboxId = 'c347948f-e1d0-4cd7-9341-f0f6ef5289bf';
          var digestId = 'e9be4a71-f6ca-4f02-b431-d74489dee5d0';
          var payload = {
            digestId: digestId
          };

          request(app)
            .post('/api/inboxes/' + inboxId + '/commits')
            .send(JSON.stringify(payload))
            .type('application/json')
            .end(shouldBehaveThusly);
        }

        it('it should provide an appropriate response', function(done) {
          postInboxWithoutXGithubEvent(function(err, res) {
            JSON.parse(res.text).errors.should.deep.equal(['Unknown event type. Please include an x-github-event header.']);
            done();
          });
        });

        it('it should have a response Content-Type of application/json', function(done) {
          postInboxWithoutXGithubEvent(function(err, res) {
            res.get('Content-Type').should.equal('application/json; charset=utf-8');
            done();
          });
        });

        it('it should have a response code of 400 (Bad Request)', function(done) {
          postInboxWithoutXGithubEvent(function(err, res) {
            res.status.should.equal(400);
            done();
          });
        });
      });

      describe('but with an error returned from eventStoreClient.streams.post', function() {
        before(function() {
          eventStoreClient.streams.post.callsArgWith(1, 'Houston, we have a problem.', null);
        });

        it('it should send back an appropriate error response', function(done) {
          postInbox(inboxPayload, function(err, res) {
            shouldBeGenericError(res);
            done();
          }, null, inboxId);
        });

        it('it should have a response Content-Type of application/json', function(done) {
          postInbox(inboxPayload, function(err, res) {
            res.get('Content-Type').should.equal('application/json; charset=utf-8');
            done();
          });
        });

        it('it should send back an appropriate error status code of 500', function(done) {
          postInbox(inboxPayload, function(err, res) {
            res.status.should.equal(500);
            done();
          });
        });
      });

      describe('but with an HTTP status code of 408 (Request Timeout) when posting to eventstore', function() {
        var response;

        before(function() {
          eventStoreClient.streams.post.callsArgWith(1, null, {
            statusCode: 408
          });

          postInbox(inboxPayload, function(err, res) {
            response = res;
          });
        })

        it('it should report that there was an internal error', function(done) {
          shouldBeGenericError(response);
          done();
        });

        it('it should report a status code of 500 (Internal Server Error)', function(done) {
          response.status.should.equal(500);
          done();
        });
      });

      describe('but with an error returned from getting the inbox id', function() {
        before(function() {
          eventStoreClient.projection.getState.callsArgWith(1, 'There was an internal error when trying to process your request.', null);
        })

        it('it should send back an appropriate error status code of 500 (Internal Server Error)', function(done) {
          postInbox(inboxPayload, function(err, res) {
            res.status.should.equal(500);
            done();
          });
        });

        it('it should report that there was an internal error.', function(done) {
          postInbox(inboxPayload, function(err, res) {
            shouldBeGenericError(res);
            done();
          });
        });

        it('it should have a response Content-Type of application/json', function(done) {
          postInbox(inboxPayload, function(err, res) {
            res.get('Content-Type').should.equal('application/json; charset=utf-8');
            done();
          });
        });
      })

      describe('but with an HTTP status code of 408 (Request Timeout) when getting information for an inbox', function() {

        var response;

        before(function() {
          eventStoreClient.projection.getState.callsArgWith(1, null, {
            statusCode: 408
          });

          postInbox(inboxPayload, function(err, res) {
            response = res;
          });
        });

        it('it should report that there was an internal problem', function(done) {
          shouldBeGenericError(response);
          done();
        });

        it('it should report a status code of 500 (Internal Server Error)', function(done) {
          response.status.should.equal(500);
          done();
        });
      });
    });

    describe('with an invalid inboxId', function() {
      before(function() {
        validator.isUUID.returns(false);
      });

      it('it should respond with a statusCode of 400.', function(done) {
        postInbox(inboxPayload, function(err, res) {
          res.status.should.equal(400);
          done();
        }, null, 'not_a_uuid');
      });

      it('it should respond with a meaningful error message.', function(done) {
        postInbox(inboxPayload, function(err, res) {
          JSON.parse(res.text).message.should.equal('The value not_a_uuid is not recognized as a valid inbox identifier.');
          done();
        }, null, 'not_a_uuid');
      });

      it('it should have a response Content-Type of application/json', function(done) {
        postInbox(inboxPayload, function(err, res) {
          res.get('Content-Type').should.equal('application/json; charset=utf-8');
          done();
        }, null, 'not_a_uuid');
      });
    });
  });

  describe('when retrieving information about an inbox (/api/inboxes/:uuid)', function() {
    var inboxId = 'c347948f-e1d0-4cd7-9341-f0f6ef5289bf';
    var digestId = 'e9be4a71-f6ca-4f02-b431-d74489dee5d0';

    describe('with an invalid, non-uuid inboxes identifier', function() {

      before(function() {
        validator.isUUID.returns(false);
      })

      function get(shouldBehaveThusly) {
        getInbox('/api/inboxes/not_a_uuid', shouldBehaveThusly);
      }

      it('it returns a 400 status code', function(done) {
        get(function(err, res) {
          res.statusCode.should.equal(400);
          done();
        });
      });

      it('it returns a meaningful error message', function(done) {
        get(function(err, res) {
          res.text.should.equal('The value "not_a_uuid" is not recognized as a valid inbox identifier.');
          done();
        });
      });
    });

    describe('with a valid, uuid inbox identifier', function() {

      var protocol = 'http';

      var responseFromEventStoreForInbox = {
        digestId: digestId,
        family: 'GitHub',
        name: 'Drive faster than fast',
        url: 'http://github.com/somewhere'
      };

      before(function() {
        validator.isUUID.returns(true);
      })

      beforeEach(function() {
        eventStoreClient.projection.getState.callsArgWith(1, null, {
          body: JSON.stringify(responseFromEventStoreForInbox),
          statusCode: 200
        });

        urls.href = sinon.stub();
        urls.href.returns(function() {});
      });

      function get(shouldBehaveThusly) {
        getInbox('/api/inboxes/' + inboxId, shouldBehaveThusly);
      }

      it('calls eventStore.projection.getState with correct parameters', function(done) {
        get(function(err, res) {
          eventStoreClient.projection.getState.should.have.been.calledWith({
            name: sinon.match.any,
            partition: 'inbox-' + inboxId
          }, sinon.match.func);
          done();
        });
      });

      it('an appropriate hypermedia response is requested', function(done) {
        var hypermediaParams = _.extend({
          inboxId: inboxId
        }, responseFromEventStoreForInbox);

        get(function(err, res) {
          hypermediaResponseStub.inboxes.uuid.GET.should.have.been.calledWith(sinon.match.func, hypermediaParams);
          done();
        });
      });

      it('it should call urls.href once.', function(done) {
        get(function(err, res) {
          urls.href.should.have.been.calledOnce;
          done();
        });
      });

      it('it returns a 200 status code', function(done) {
        get(function(err, res) {
          res.statusCode.should.equal(200);
          done();
        });
      });

      it('returns a Content-Type of application/hal+json', function(done) {
        get(function(err, res) {
          res.get('Content-Type').should.equal('application/hal+json; charset=utf-8');
          done();
        });
      });
    });

    describe('with a valid, uuid that does not match a real inbox', function() {

      beforeEach(function() {
        eventStoreClient.projection.getState.callsArgWith(1, null, {
          body: ''
        });
      });

      function get(shouldBehaveThusly) {
        getInbox('/api/inboxes/' + inboxId, shouldBehaveThusly);
      }

      it('calls eventStore.projection.getState with correct parameters', function(done) {
        get(function(err, res) {
          eventStoreClient.projection.getState.should.have.been.calledWith({
            name: sinon.match.any,
            partition: 'inbox-' + inboxId
          }, sinon.match.any);
          done();
        });
      });

      it('it returns a 404 status code', function(done) {
        get(function(err, res) {
          res.statusCode.should.equal(404);
          done();
        });
      });

      it('returns a Content-Type of application/json', function(done) {
        get(function(err, res) {
          res.get('Content-Type').should.equal('application/json; charset=utf-8');
          done();
        });
      });

      it('it returns a meaningful error message', function(done) {
        get(function(err, res) {
          res.text.should.equal(JSON.stringify({
            'error': 'Could not find an inbox with id ' + inboxId
          }));
          done();
        });
      });

    });

    describe('with an error returned from eventStoreClient', function() {

      beforeEach(function() {
        eventStoreClient.projection.getState.callsArgWith(1, 'Hey there is an error!', {});
      });

      function get(shouldBehaveThusly) {
        getInbox('/api/inboxes/' + inboxId, shouldBehaveThusly);
      }

      it('calls eventStore.projection.getState with correct parameters', function(done) {
        get(function(err, res) {
          eventStoreClient.projection.getState.should.have.been.calledWith({
            name: sinon.match.any,
            partition: 'inbox-' + inboxId
          }, sinon.match.any);
          done();
        });
      });

      it('it returns a 500 status code', function(done) {
        get(function(err, res) {
          res.statusCode.should.equal(500);
          done();
        });
      });

      it('returns a Content-Type of application/json', function(done) {
        get(function(err, res) {
          res.get('Content-Type').should.equal('application/json; charset=utf-8');
          done();
        });
      });

      it('it returns a meaningful error message', function(done) {
        get(function(err, res) {
          shouldBeGenericError(res);
          done();
        });
      });
    });

    describe('and an HTTP 408 timeout occurs (Request Timeout) when retrieving information about the inbox', function() {
      var response;

      before(function() {
        eventStoreClient.projection.getState.callsArgWith(1, null, {
          statusCode: 408
        });
      })

      function get(shouldBehaveThusly) {
        getInbox('/api/inboxes/' + inboxId, shouldBehaveThusly);
      }

      it('it should report that there was an internal problem', function(done) {
        get(function(error, response) {
          shouldBeGenericError(response);
          done();
        });

      });

      it('it should report a status code of 500 (Internal Server Error)', function(done) {
        get(function(error, response) {
          response.status.should.equal(500);
          done();
        });
      });
    });

  });

});


text:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using NPoco;
using NUnit.Framework;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Persistence.Querying;
using Umbraco.Cms.Core.PropertyEditors;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
using Umbraco.Cms.Infrastructure.PublishedCache;
using Umbraco.Cms.Tests.Common;
using Umbraco.Cms.Tests.Common.Builders;
using Umbraco.Cms.Tests.Common.Testing;
using Umbraco.Cms.Tests.Integration.Testing;
using Umbraco.Extensions;
using Constants = Umbraco.Cms.Core.Constants;

namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services
{
    [TestFixture]
    [Category("Slow")]
    [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true, WithApplication = true)]
    public class MemberServiceTests : UmbracoIntegrationTest
    {
        private IMemberTypeService MemberTypeService => GetRequiredService<IMemberTypeService>();

        private IMemberService MemberService => GetRequiredService<IMemberService>();

        [Test]
        public void Can_Update_Member_Property_Values()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "hello", "helloworld@test123.com", "hello", "hello");
            member.SetValue("title", "title of mine");
            member.SetValue("bodyText", "hello world");
            MemberService.Save(member);

            // re-get
            member = MemberService.GetById(member.Id);
            member.SetValue("title", "another title of mine");          // Change a value
            member.SetValue("bodyText", null);                          // Clear a value
            member.SetValue("author", "new author");                    // Add a value
            MemberService.Save(member);

            // re-get
            member = MemberService.GetById(member.Id);
            Assert.AreEqual("another title of mine", member.GetValue("title"));
            Assert.IsNull(member.GetValue("bodyText"));
            Assert.AreEqual("new author", member.GetValue("author"));
        }

        [Test]
        public void Can_Get_By_Username()
        {
            IMemberType memberType = MemberTypeService.Get("member");
            IMember member = new Member("xname", "xemail", "xusername", "xrawpassword", memberType, true);
            MemberService.Save(member);

            IMember member2 = MemberService.GetByUsername(member.Username);

            Assert.IsNotNull(member2);
            Assert.AreEqual(member.Email, member2.Email);
        }

        [Test]
        public void Can_Set_Last_Login_Date()
        {
            DateTime now = DateTime.Now;
            IMemberType memberType = MemberTypeService.Get("member");
            IMember member = new Member("xname", "xemail", "xusername", "xrawpassword", memberType, true)
            {
                LastLoginDate = now,
                UpdateDate = now
            };
            MemberService.Save(member);

            DateTime newDate = now.AddDays(10);
            MemberService.SetLastLogin(member.Username, newDate);

            // re-get
            member = MemberService.GetById(member.Id);

            Assert.That(member.LastLoginDate, Is.EqualTo(newDate).Within(1).Seconds);
            Assert.That(member.UpdateDate, Is.EqualTo(newDate).Within(1).Seconds);
        }

        [Test]
        public void Can_Create_Member_With_Properties()
        {
            IMemberType memberType = MemberTypeService.Get("member");
            IMember member = new Member("xname", "xemail", "xusername", "xrawpassword", memberType, true);
            MemberService.Save(member);

            member = MemberService.GetById(member.Id);
            Assert.AreEqual("xemail", member.Email);

            var contentTypeFactory = new PublishedContentTypeFactory(new NoopPublishedModelFactory(), new PropertyValueConverterCollection(() => Enumerable.Empty<IPropertyValueConverter>()), GetRequiredService<IDataTypeService>());
            var pmemberType = new PublishedContentType(memberType, contentTypeFactory);

            var publishedSnapshotAccessor = new TestPublishedSnapshotAccessor();
            var variationContextAccessor = new TestVariationContextAccessor();
            IPublishedContent pmember = PublishedMember.Create(member, pmemberType, false, publishedSnapshotAccessor, variationContextAccessor, GetRequiredService<IPublishedModelFactory>());

            // contains the umbracoMember... properties created when installing, on the member type
            // contains the other properties, that PublishedContentType adds (BuiltinMemberProperties)
            //
            // TODO: see TODO in PublishedContentType, this list contains duplicates
            string[] aliases = new[]
            {
                Constants.Conventions.Member.Comments,
                Constants.Conventions.Member.FailedPasswordAttempts,
                Constants.Conventions.Member.IsApproved,
                Constants.Conventions.Member.IsLockedOut,
                Constants.Conventions.Member.LastLockoutDate,
                Constants.Conventions.Member.LastLoginDate,
                Constants.Conventions.Member.LastPasswordChangeDate,
                nameof(IMember.Email),
                nameof(IMember.Username),
                nameof(IMember.Comments),
                nameof(IMember.IsApproved),
                nameof(IMember.IsLockedOut),
                nameof(IMember.LastLockoutDate),
                nameof(IMember.CreateDate),
                nameof(IMember.LastLoginDate),
                nameof(IMember.LastPasswordChangeDate)
            };

            var properties = pmember.Properties.ToList();

            Assert.IsTrue(properties.Select(x => x.Alias).ContainsAll(aliases));

            IPublishedProperty email = properties[aliases.IndexOf(nameof(IMember.Email))];
            Assert.AreEqual("xemail", email.GetSourceValue());
        }

        [Test]
        public void Can_Create_Member()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            Assert.AreNotEqual(0, member.Id);
            IMember foundMember = MemberService.GetById(member.Id);
            Assert.IsNotNull(foundMember);
            Assert.AreEqual("test@test.com", foundMember.Email);
        }

        [Test]
        public void Can_Create_Member_With_Long_TLD_In_Email()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.marketing", "pass", "test");
            MemberService.Save(member);

            Assert.AreNotEqual(0, member.Id);
            IMember foundMember = MemberService.GetById(member.Id);
            Assert.IsNotNull(foundMember);
            Assert.AreEqual("test@test.marketing", foundMember.Email);
        }

        [Test]
        public void Can_Create_Role()
        {
            MemberService.AddRole("MyTestRole");

            IEnumerable<IMemberGroup> found = MemberService.GetAllRoles();

            Assert.AreEqual(1, found.Count());
            Assert.AreEqual("MyTestRole", found.Single().Name);
        }

        [Test]
        public void Can_Create_Duplicate_Role()
        {
            MemberService.AddRole("MyTestRole");
            MemberService.AddRole("MyTestRole");

            IEnumerable<IMemberGroup> found = MemberService.GetAllRoles();

            Assert.AreEqual(1, found.Count());
            Assert.AreEqual("MyTestRole", found.Single().Name);
        }

        [Test]
        public void Can_Get_All_Roles()
        {
            MemberService.AddRole("MyTestRole1");
            MemberService.AddRole("MyTestRole2");
            MemberService.AddRole("MyTestRole3");

            IEnumerable<IMemberGroup> found = MemberService.GetAllRoles();

            Assert.AreEqual(3, found.Count());
        }

        [Test]
        public void Can_Get_All_Roles_IDs()
        {
            MemberService.AddRole("MyTestRole1");
            MemberService.AddRole("MyTestRole2");
            MemberService.AddRole("MyTestRole3");

            IEnumerable<int> found = MemberService.GetAllRolesIds();

            Assert.AreEqual(3, found.Count());
        }

        [Test]
        public void Can_Replace_Roles()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            string[] roleNames1 = new[] { "TR1", "TR2" };
            MemberService.AssignRoles(new[] { member.Id }, roleNames1);
            IEnumerable<string> memberRoles = MemberService.GetAllRoles(member.Id);
            CollectionAssert.AreEquivalent(roleNames1, memberRoles);

            string[] roleNames2 = new[] { "TR3", "TR4" };
            MemberService.ReplaceRoles(new[] { member.Id }, roleNames2);
            memberRoles = MemberService.GetAllRoles(member.Id);
            CollectionAssert.AreEquivalent(roleNames2, memberRoles);
        }

        [Test]
        public void Can_Get_All_Roles_By_Member_Id()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            MemberService.AddRole("MyTestRole1");
            MemberService.AddRole("MyTestRole2");
            MemberService.AddRole("MyTestRole3");
            MemberService.AssignRoles(new[] { member.Id }, new[] { "MyTestRole1", "MyTestRole2" });

            IEnumerable<string> memberRoles = MemberService.GetAllRoles(member.Id);

            Assert.AreEqual(2, memberRoles.Count());
        }

        [Test]
        public void Can_Get_All_Roles_Ids_By_Member_Id()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            MemberService.AddRole("MyTestRole1");
            MemberService.AddRole("MyTestRole2");
            MemberService.AddRole("MyTestRole3");
            MemberService.AssignRoles(new[] { member.Id }, new[] { "MyTestRole1", "MyTestRole2" });

            IEnumerable<int> memberRoles = MemberService.GetAllRolesIds(member.Id);

            Assert.AreEqual(2, memberRoles.Count());
        }

        [Test]
        public void Can_Get_All_Roles_By_Member_Username()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            // need to test with '@' symbol in the lookup
            IMember member2 = MemberBuilder.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2@test.com");
            MemberService.Save(member2);

            MemberService.AddRole("MyTestRole1");
            MemberService.AddRole("MyTestRole2");
            MemberService.AddRole("MyTestRole3");
            MemberService.AssignRoles(new[] { member.Id, member2.Id }, new[] { "MyTestRole1", "MyTestRole2" });

            IEnumerable<string> memberRoles = MemberService.GetAllRoles("test");
            Assert.AreEqual(2, memberRoles.Count());

            IEnumerable<string> memberRoles2 = MemberService.GetAllRoles("test2@test.com");
            Assert.AreEqual(2, memberRoles2.Count());
        }

        [Test]
        public void Can_Delete_Role()
        {
            MemberService.AddRole("MyTestRole1");

            MemberService.DeleteRole("MyTestRole1", false);

            IEnumerable<IMemberGroup> memberRoles = MemberService.GetAllRoles();

            Assert.AreEqual(0, memberRoles.Count());
        }

        [Test]
        public void Throws_When_Deleting_Assigned_Role()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            MemberService.AddRole("MyTestRole1");
            MemberService.AssignRoles(new[] { member.Id }, new[] { "MyTestRole1", "MyTestRole2" });

            Assert.Throws<InvalidOperationException>(() => MemberService.DeleteRole("MyTestRole1", true));
        }

        [Test]
        public void Can_Get_Members_In_Role()
        {
            MemberService.AddRole("MyTestRole1");
            int roleId;
            using (IScope scope = ScopeProvider.CreateScope())
            {
                roleId = scope.Database.ExecuteScalar<int>("SELECT id from umbracoNode where [text] = 'MyTestRole1'");
                scope.Complete();
            }

            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            Member member1 = MemberBuilder.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1");
            MemberService.Save(member1);
            Member member2 = MemberBuilder.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2");
            MemberService.Save(member2);

            using (IScope scope = ScopeProvider.CreateScope())
            {
                scope.Database.Insert(new Member2MemberGroupDto { MemberGroup = roleId, Member = member1.Id });
                scope.Database.Insert(new Member2MemberGroupDto { MemberGroup = roleId, Member = member2.Id });
                scope.Complete();
            }

            IEnumerable<IMember> membersInRole = MemberService.GetMembersInRole("MyTestRole1");
            Assert.AreEqual(2, membersInRole.Count());
        }

        [Test]
        public void Cannot_Save_Member_With_Empty_Name()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, string.Empty, "test@test.com", "pass", "test");

            // Act & Assert
            Assert.Throws<ArgumentException>(() => MemberService.Save(member));
        }

        [TestCase("MyTestRole1", "test1", StringPropertyMatchType.StartsWith, 1)]
        [TestCase("MyTestRole1", "test", StringPropertyMatchType.StartsWith, 3)]
        [TestCase("MyTestRole1", "test1", StringPropertyMatchType.Exact, 1)]
        [TestCase("MyTestRole1", "test", StringPropertyMatchType.Exact, 0)]
        [TestCase("MyTestRole1", "st2", StringPropertyMatchType.EndsWith, 1)]
        [TestCase("MyTestRole1", "test%", StringPropertyMatchType.Wildcard, 3)]
        public void Find_Members_In_Role(string roleName1, string usernameToMatch, StringPropertyMatchType matchType, int resultCount)
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            Member member1 = MemberBuilder.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1");
            MemberService.Save(member1);
            Member member2 = MemberBuilder.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2");
            MemberService.Save(member2);
            Member member3 = MemberBuilder.CreateSimpleMember(memberType, "test3", "test3@test.com", "pass", "test3");
            MemberService.Save(member3);

            MemberService.AssignRoles(new[] { member1.Id, member2.Id, member3.Id }, new[] { roleName1 });

            IEnumerable<IMember> result = MemberService.FindMembersInRole(roleName1, usernameToMatch, matchType);
            Assert.AreEqual(resultCount, result.Count());
        }

        [Test]
        public void Associate_Members_To_Roles_With_Member_Id()
        {
            MemberService.AddRole("MyTestRole1");

            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            Member member1 = MemberBuilder.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1");
            MemberService.Save(member1);
            Member member2 = MemberBuilder.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2");
            MemberService.Save(member2);

            // temp make sure they exist
            Assert.IsNotNull(MemberService.GetById(member1.Id));
            Assert.IsNotNull(MemberService.GetById(member2.Id));

            MemberService.AssignRoles(new[] { member1.Id, member2.Id }, new[] { "MyTestRole1" });

            IEnumerable<IMember> membersInRole = MemberService.GetMembersInRole("MyTestRole1");

            Assert.AreEqual(2, membersInRole.Count());
        }

        [Test]
        public void Associate_Members_To_Roles_With_Member_Id_Casing()
        {
            MemberService.AddRole("MyTestRole1");

            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            Member member1 = MemberBuilder.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1");
            MemberService.Save(member1);
            Member member2 = MemberBuilder.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2");
            MemberService.Save(member2);

            // temp make sure they exist
            Assert.IsNotNull(MemberService.GetById(member1.Id));
            Assert.IsNotNull(MemberService.GetById(member2.Id));

            MemberService.AssignRoles(new[] { member1.Id, member2.Id }, new[] { "mytestrole1" });

            IEnumerable<IMember> membersInRole = MemberService.GetMembersInRole("MyTestRole1");

            Assert.AreEqual(2, membersInRole.Count());
        }

        [Test]
        public void Associate_Members_To_Roles_With_Member_Username()
        {
            MemberService.AddRole("MyTestRole1");

            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            Member member1 = MemberBuilder.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1");
            MemberService.Save(member1);
            Member member2 = MemberBuilder.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2");
            MemberService.Save(member2);

            MemberService.AssignRoles(new[] { member1.Username, member2.Username }, new[] { "MyTestRole1" });

            IEnumerable<IMember> membersInRole = MemberService.GetMembersInRole("MyTestRole1");

            Assert.AreEqual(2, membersInRole.Count());
        }

        [Test]
        public void Associate_Members_To_Roles_With_Member_Username_Containing_At_Symbols()
        {
            MemberService.AddRole("MyTestRole1");

            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            Member member1 = MemberBuilder.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1@test.com");
            MemberService.Save(member1);
            Member member2 = MemberBuilder.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2@test.com");
            MemberService.Save(member2);

            MemberService.AssignRoles(new[] { member1.Username, member2.Username }, new[] { "MyTestRole1" });

            IEnumerable<IMember> membersInRole = MemberService.GetMembersInRole("MyTestRole1");

            Assert.AreEqual(2, membersInRole.Count());
        }

        [Test]
        public void Associate_Members_To_Roles_With_New_Role()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            Member member1 = MemberBuilder.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1");
            MemberService.Save(member1);
            Member member2 = MemberBuilder.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2");
            MemberService.Save(member2);

            // implicitly create the role
            MemberService.AssignRoles(new[] { member1.Username, member2.Username }, new[] { "MyTestRole1" });

            IEnumerable<IMember> membersInRole = MemberService.GetMembersInRole("MyTestRole1");

            Assert.AreEqual(2, membersInRole.Count());
        }

        [Test]
        public void Remove_Members_From_Roles_With_Member_Id()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            Member member1 = MemberBuilder.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1");
            MemberService.Save(member1);
            Member member2 = MemberBuilder.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2");
            MemberService.Save(member2);

            MemberService.AssignRoles(new[] { member1.Id, member2.Id }, new[] { "MyTestRole1", "MyTestRole2" });

            MemberService.DissociateRoles(new[] { member1.Id }, new[] { "MyTestRole1" });
            MemberService.DissociateRoles(new[] { member1.Id, member2.Id }, new[] { "MyTestRole2" });

            IEnumerable<IMember> membersInRole = MemberService.GetMembersInRole("MyTestRole1");
            Assert.AreEqual(1, membersInRole.Count());
            membersInRole = MemberService.GetMembersInRole("MyTestRole2");
            Assert.AreEqual(0, membersInRole.Count());
        }

        [Test]
        public void Remove_Members_From_Roles_With_Member_Username()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            Member member1 = MemberBuilder.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1");
            MemberService.Save(member1);
            Member member2 = MemberBuilder.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2");
            MemberService.Save(member2);

            MemberService.AssignRoles(new[] { member1.Username, member2.Username }, new[] { "MyTestRole1", "MyTestRole2" });

            MemberService.DissociateRoles(new[] { member1.Username }, new[] { "MyTestRole1" });
            MemberService.DissociateRoles(new[] { member1.Username, member2.Username }, new[] { "MyTestRole2" });

            IEnumerable<IMember> membersInRole = MemberService.GetMembersInRole("MyTestRole1");
            Assert.AreEqual(1, membersInRole.Count());
            membersInRole = MemberService.GetMembersInRole("MyTestRole2");
            Assert.AreEqual(0, membersInRole.Count());
        }

        [Test]
        public void Can_Delete_member()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            MemberService.Delete(member);
            IMember deleted = MemberService.GetById(member.Id);

            // Assert
            Assert.That(deleted, Is.Null);
        }

        [Test]
        public void Exists_By_Username()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);
            IMember member2 = MemberBuilder.CreateSimpleMember(memberType, "test", "test2@test.com", "pass", "test2@test.com");
            MemberService.Save(member2);

            Assert.IsTrue(MemberService.Exists("test"));
            Assert.IsFalse(MemberService.Exists("notFound"));
            Assert.IsTrue(MemberService.Exists("test2@test.com"));
        }

        [Test]
        public void Exists_By_Id()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            Assert.IsTrue(MemberService.Exists(member.Id));
            Assert.IsFalse(MemberService.Exists(9876));
        }

        [Test]
        public void Tracks_Dirty_Changes()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            IMember resolved = MemberService.GetByEmail(member.Email);

            // NOTE: This will not trigger a property isDirty because this is not based on a 'Property', it is
            // just a c# property of the Member object
            resolved.Email = "changed@test.com";

            // NOTE: this WILL trigger a property isDirty because setting this c# property actually sets a value of
            // the underlying 'Property'
            resolved.FailedPasswordAttempts = 1234;

            var dirtyMember = (ICanBeDirty)resolved;
            var dirtyProperties = resolved.Properties.Where(x => x.IsDirty()).ToList();
            Assert.IsTrue(dirtyMember.IsDirty());
            Assert.AreEqual(1, dirtyProperties.Count);
        }

        [Test]
        public void Get_By_Email()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            Assert.IsNotNull(MemberService.GetByEmail(member.Email));
            Assert.IsNull(MemberService.GetByEmail("do@not.find"));
        }

        [Test]
        public void Get_Member_Name()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "Test Real Name", "test@test.com", "pass", "testUsername");
            MemberService.Save(member);

            Assert.AreEqual("Test Real Name", member.Name);
        }

        [Test]
        public void Get_By_Username()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            Assert.IsNotNull(MemberService.GetByUsername(member.Username));
            Assert.IsNull(MemberService.GetByUsername("notFound"));
        }

        [Test]
        public void Get_By_Object_Id()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IMember member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "pass", "test");
            MemberService.Save(member);

            Assert.IsNotNull(MemberService.GetById(member.Id));
            Assert.IsNull(MemberService.GetById(9876));
        }

        [Test]
        public void Get_All_Paged_Members()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);

            IEnumerable<IMember> found = MemberService.GetAll(0, 2, out long totalRecs);

            Assert.AreEqual(2, found.Count());
            Assert.AreEqual(10, totalRecs);
            Assert.AreEqual("test0", found.First().Username);
            Assert.AreEqual("test1", found.Last().Username);
        }

        [Test]
        public void Get_All_Paged_Members_With_Filter()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);

            IEnumerable<IMember> found = MemberService.GetAll(0, 2, out long totalRecs, "username", Direction.Ascending, true, null, "Member No-");

            Assert.AreEqual(2, found.Count());
            Assert.AreEqual(10, totalRecs);
            Assert.AreEqual("test0", found.First().Username);
            Assert.AreEqual("test1", found.Last().Username);

            found = MemberService.GetAll(0, 2, out totalRecs, "username", Direction.Ascending, true, null, "Member No-5");

            Assert.AreEqual(1, found.Count());
            Assert.AreEqual(1, totalRecs);
            Assert.AreEqual("test5", found.First().Username);
        }

        [Test]
        public void Find_By_Name_Starts_With()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "Bob", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.FindMembersByDisplayName("B", 0, 100, out long totalRecs, StringPropertyMatchType.StartsWith);

            Assert.AreEqual(1, found.Count());
        }

        [Test]
        public void Find_By_Email_Starts_With()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);

            // don't find this
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.FindByEmail("tes", 0, 100, out long totalRecs, StringPropertyMatchType.StartsWith);

            Assert.AreEqual(10, found.Count());
        }

        [Test]
        public void Find_By_Email_Ends_With()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);

            // include this
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.FindByEmail("test.com", 0, 100, out long totalRecs, StringPropertyMatchType.EndsWith);

            Assert.AreEqual(11, found.Count());
        }

        [Test]
        public void Find_By_Email_Contains()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);

            // include this
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.FindByEmail("test", 0, 100, out long totalRecs, StringPropertyMatchType.Contains);

            Assert.AreEqual(11, found.Count());
        }

        [Test]
        public void Find_By_Email_Exact()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);

            // include this
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.FindByEmail("hello@test.com", 0, 100, out long totalRecs, StringPropertyMatchType.Exact);

            Assert.AreEqual(1, found.Count());
        }

        [Test]
        public void Find_By_Login_Starts_With()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);

            // don't find this
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.FindByUsername("tes", 0, 100, out long totalRecs, StringPropertyMatchType.StartsWith);

            Assert.AreEqual(10, found.Count());
        }

        [Test]
        public void Find_By_Login_Ends_With()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);

            // include this
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.FindByUsername("llo", 0, 100, out long totalRecs, StringPropertyMatchType.EndsWith);

            Assert.AreEqual(1, found.Count());
        }

        [Test]
        public void Find_By_Login_Contains()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);

            // include this
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hellotest");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.FindByUsername("test", 0, 100, out long totalRecs, StringPropertyMatchType.Contains);

            Assert.AreEqual(11, found.Count());
        }

        [Test]
        public void Find_By_Login_Exact()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);

            // include this
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.FindByUsername("hello", 0, 100, out long totalRecs, StringPropertyMatchType.Exact);

            Assert.AreEqual(1, found.Count());
        }

        [Test]
        public void Get_By_Property_String_Value_Exact()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "title", "hello member", StringPropertyMatchType.Exact);

            Assert.AreEqual(1, found.Count());
        }

        [Test]
        public void Get_By_Property_String_Value_Contains()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "title", " member", StringPropertyMatchType.Contains);

            Assert.AreEqual(11, found.Count());
        }

        [Test]
        public void Get_By_Property_String_Value_Starts_With()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "title", "Member No", StringPropertyMatchType.StartsWith);

            Assert.AreEqual(10, found.Count());
        }

        [Test]
        public void Get_By_Property_String_Value_Ends_With()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue("title", "title of mine");
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "title", "mine", StringPropertyMatchType.EndsWith);

            Assert.AreEqual(1, found.Count());
        }

        [Test]
        public void Get_By_Property_Int_Value_Exact()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            memberType.AddPropertyType(
                new PropertyType(
                    ShortStringHelper,
                    Constants.PropertyEditors.Aliases.Integer,
                    ValueStorageType.Integer,
                    "number")
                    {
                        Name = "Number",
                        DataTypeId = -51,  // NOTE: This is what really determines the db type - the above definition doesn't really do anything
                    }, "content", "Content");
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.SetValue("number", i));
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue("number", 2);
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "number", 2, ValuePropertyMatchType.Exact);

            Assert.AreEqual(2, found.Count());
        }

        [Test]
        public void Get_By_Property_Int_Value_Greater_Than()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            memberType.AddPropertyType(
                new PropertyType(
                    ShortStringHelper,
                    Constants.PropertyEditors.Aliases.Integer,
                    ValueStorageType.Integer,
                    "number")
                    {
                        Name = "Number",
                        DataTypeId = -51,  // NOTE: This is what really determines the db type - the above definition doesn't really do anything
                    }, "content", "Content");
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.SetValue("number", i));
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue("number", 10);
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "number", 3, ValuePropertyMatchType.GreaterThan);

            Assert.AreEqual(7, found.Count());
        }

        [Test]
        public void Get_By_Property_Int_Value_Greater_Than_Equal_To()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            memberType.AddPropertyType(
                new PropertyType(
                    ShortStringHelper,
                    Constants.PropertyEditors.Aliases.Integer,
                    ValueStorageType.Integer,
                    "number")
                {
                    Name = "Number",
                    DataTypeId = -51,  // NOTE: This is what really determines the db type - the above definition doesn't really do anything
                }, "content", "Content");
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.SetValue("number", i));
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue("number", 10);
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "number", 3, ValuePropertyMatchType.GreaterThanOrEqualTo);

            Assert.AreEqual(8, found.Count());
        }

        [Test]
        public void Get_By_Property_Int_Value_Less_Than()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            memberType.AddPropertyType(
                new PropertyType(
                    ShortStringHelper,
                    Constants.PropertyEditors.Aliases.DateTime,
                    ValueStorageType.Date,
                    "number")
                    {
                        Name = "Number",
                        DataTypeId = -51,  // NOTE: This is what really determines the db type - the above definition doesn't really do anything
                    }, "content", "Content");
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.SetValue("number", i));
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue("number", 1);
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "number", 5, ValuePropertyMatchType.LessThan);

            Assert.AreEqual(6, found.Count());
        }

        [Test]
        public void Get_By_Property_Int_Value_Less_Than_Or_Equal()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            memberType.AddPropertyType(
                new PropertyType(
                    ShortStringHelper,
                    Constants.PropertyEditors.Aliases.Integer,
                    ValueStorageType.Integer,
                    "number")
                    {
                        Name = "Number",
                        DataTypeId = -51,  // NOTE: This is what really determines the db type - the above definition doesn't really do anything
                    }, "content", "Content");
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.SetValue("number", i));
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue("number", 1);
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "number", 5, ValuePropertyMatchType.LessThanOrEqualTo);

            Assert.AreEqual(7, found.Count());
        }

        [Test]
        public void Get_By_Property_Date_Value_Exact()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            memberType.AddPropertyType(
                new PropertyType(
                    ShortStringHelper,
                    Constants.PropertyEditors.Aliases.Integer,
                    ValueStorageType.Integer,
                    "date")
                    {
                        Name = "Date",
                        DataTypeId = -36,  // NOTE: This is what really determines the db type - the above definition doesn't really do anything
                    }, "content", "Content");
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.SetValue("date", new DateTime(2013, 12, 20, 1, i, 0)));
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue("date", new DateTime(2013, 12, 20, 1, 2, 0));
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "date", new DateTime(2013, 12, 20, 1, 2, 0), ValuePropertyMatchType.Exact);

            Assert.AreEqual(2, found.Count());
        }

        [Test]
        public void Get_By_Property_Date_Value_Greater_Than()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            memberType.AddPropertyType(
                new PropertyType(
                    ShortStringHelper,
                    Constants.PropertyEditors.Aliases.Integer,
                    ValueStorageType.Integer,
                    "date")
                    {
                        Name = "Date",
                        DataTypeId = -36,  // NOTE: This is what really determines the db type - the above definition doesn't really do anything
                    }, "content", "Content");
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.SetValue("date", new DateTime(2013, 12, 20, 1, i, 0)));
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue("date", new DateTime(2013, 12, 20, 1, 10, 0));
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "date", new DateTime(2013, 12, 20, 1, 3, 0), ValuePropertyMatchType.GreaterThan);

            Assert.AreEqual(7, found.Count());
        }

        [Test]
        public void Get_By_Property_Date_Value_Greater_Than_Equal_To()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            memberType.AddPropertyType(
                new PropertyType(
                    ShortStringHelper,
                    Constants.PropertyEditors.Aliases.Integer,
                    ValueStorageType.Integer,
                    "date")
                    {
                        Name = "Date",
                        DataTypeId = -36,  // NOTE: This is what really determines the db type - the above definition doesn't really do anything
                    }, "content", "Content");
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.SetValue("date", new DateTime(2013, 12, 20, 1, i, 0)));
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue("date", new DateTime(2013, 12, 20, 1, 10, 0));
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "date", new DateTime(2013, 12, 20, 1, 3, 0), ValuePropertyMatchType.GreaterThanOrEqualTo);

            Assert.AreEqual(8, found.Count());
        }

        [Test]
        public void Get_By_Property_Date_Value_Less_Than()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            memberType.AddPropertyType(
                new PropertyType(
                    ShortStringHelper,
                    Constants.PropertyEditors.Aliases.Integer,
                    ValueStorageType.Integer,
                    "date")
                    {
                        Name = "Date",
                        DataTypeId = -36,  // NOTE: This is what really determines the db type - the above definition doesn't really do anything
                    }, "content", "Content");
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.SetValue("date", new DateTime(2013, 12, 20, 1, i, 0)));
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue("date", new DateTime(2013, 12, 20, 1, 1, 0));
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "date", new DateTime(2013, 12, 20, 1, 5, 0), ValuePropertyMatchType.LessThan);

            Assert.AreEqual(6, found.Count());
        }

        [Test]
        public void Get_By_Property_Date_Value_Less_Than_Or_Equal()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            memberType.AddPropertyType(
                new PropertyType(
                    ShortStringHelper,
                    Constants.PropertyEditors.Aliases.Integer,
                    ValueStorageType.Integer,
                    "date")
                    {
                        Name = "Date",
                        DataTypeId = -36,  // NOTE: This is what really determines the db type - the above definition doesn't really do anything
                    }, "content", "Content");
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.SetValue("date", new DateTime(2013, 12, 20, 1, i, 0)));
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue("date", new DateTime(2013, 12, 20, 1, 1, 0));
            MemberService.Save(customMember);

            IEnumerable<IMember> found = MemberService.GetMembersByPropertyValue(
                "date", new DateTime(2013, 12, 20, 1, 5, 0), ValuePropertyMatchType.LessThanOrEqualTo);

            Assert.AreEqual(7, found.Count());
        }

        [Test]
        public void Count_All_Members()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10);
            MemberService.Save(members);
            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            int found = MemberService.GetCount(MemberCountType.All);

            Assert.AreEqual(11, found);
        }

        [Test]
        public void Count_All_Locked_Members()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.IsLockedOut = i % 2 == 0);
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue(Constants.Conventions.Member.IsLockedOut, true);
            MemberService.Save(customMember);

            int found = MemberService.GetCount(MemberCountType.LockedOut);

            Assert.AreEqual(6, found);
        }

        [Test]
        public void Count_All_Approved_Members()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            IEnumerable<IMember> members = MemberBuilder.CreateMultipleSimpleMembers(memberType, 10, (i, member) => member.IsApproved = i % 2 == 0);
            MemberService.Save(members);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            customMember.SetValue(Constants.Conventions.Member.IsApproved, false);
            MemberService.Save(customMember);

            int found = MemberService.GetCount(MemberCountType.Approved);

            Assert.AreEqual(5, found);
        }

        [Test]
        public void Setting_Property_On_Built_In_Member_Property_When_Property_Doesnt_Exist_On_Type_Is_Ok()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            memberType.RemovePropertyType(Constants.Conventions.Member.Comments);
            MemberTypeService.Save(memberType);
            Assert.IsFalse(memberType.PropertyTypes.Any(x => x.Alias == Constants.Conventions.Member.Comments));

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");

            // this should not throw an exception
            customMember.Comments = "hello world";
            MemberService.Save(customMember);

            IMember found = MemberService.GetById(customMember.Id);

            Assert.IsTrue(found.Comments.IsNullOrWhiteSpace());
        }

        /// <summary>
        /// Because we are forcing some of the built-ins to be Labels which have an underlying db type as nvarchar but we need
        /// to ensure that the dates/int get saved to the correct column anyways.
        /// </summary>
        [Test]
        public void Setting_DateTime_Property_On_Built_In_Member_Property_Saves_To_Correct_Column()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            Member member = MemberBuilder.CreateSimpleMember(memberType, "test", "test@test.com", "test", "test");
            DateTime date = DateTime.Now;
            member.LastLoginDate = DateTime.Now;
            MemberService.Save(member);

            IMember result = MemberService.GetById(member.Id);
            Assert.AreEqual(
                date.TruncateTo(DateTimeExtensions.DateTruncate.Second),
                result.LastLoginDate.TruncateTo(DateTimeExtensions.DateTruncate.Second));

            // now ensure the col is correct
            ISqlContext sqlContext = GetRequiredService<ISqlContext>();
            Sql<ISqlContext> sql = sqlContext.Sql().Select<PropertyDataDto>()
                .From<PropertyDataDto>()
                .InnerJoin<PropertyTypeDto>().On<PropertyDataDto, PropertyTypeDto>(dto => dto.PropertyTypeId, dto => dto.Id)
                .InnerJoin<ContentVersionDto>().On<PropertyDataDto, ContentVersionDto>((left, right) => left.VersionId == right.Id)
                .Where<ContentVersionDto>(dto => dto.NodeId == member.Id)
                .Where<PropertyTypeDto>(dto => dto.Alias == Constants.Conventions.Member.LastLoginDate);

            List<PropertyDataDto> colResult;
            using (IScope scope = ScopeProvider.CreateScope())
            {
                colResult = scope.Database.Fetch<PropertyDataDto>(sql);
                scope.Complete();
            }

            Assert.AreEqual(1, colResult.Count);
            Assert.IsTrue(colResult.First().DateValue.HasValue);
            Assert.IsFalse(colResult.First().IntegerValue.HasValue);
            Assert.IsNull(colResult.First().TextValue);
            Assert.IsNull(colResult.First().VarcharValue);
        }

        [Test]
        public void New_Member_Approved_By_Default()
        {
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);

            Member customMember = MemberBuilder.CreateSimpleMember(memberType, "hello", "hello@test.com", "hello", "hello");
            MemberService.Save(customMember);

            IMember found = MemberService.GetById(customMember.Id);

            Assert.IsTrue(found.IsApproved);
        }

        [Test]
        public void Can_CreateWithIdentity()
        {
            // Arrange
            IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType();
            MemberTypeService.Save(memberType);
            string username = Path.GetRandomFileName();

            // Act
            IMember member = MemberService.CreateMemberWithIdentity(username, $"{username}@domain.email", Path.GetFileNameWithoutExtension(username), memberType);
            IMember found = MemberService.GetById(member.Id);

            // Assert
            Assert.IsNotNull(member, "Verifying a member instance has been created");
            Assert.IsNotNull(found, "Verifying the created member instance has been retrieved");
            Assert.IsTrue(found?.Name == member?.Name, "Verifying the retrieved member instance has the expected name");
        }
    }
}

redpajama_set_name: RedPajamaGithub


text:
using System;
using System.Diagnostics;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace ConfigWithStartWizard.UI {
    /// <summary>
    /// Interaction logic for SignOnStatus.xaml
    /// </summary>
    public partial class SignOnStatus : UserControl {
        public SignOnStatus() {
            InitializeComponent();
        }

        private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) {
            try {
                if (this.ActivePortalURLText.Text != "No portal set") {
                    Process.Start(new ProcessStartInfo(this.ActivePortalURLText.Text));
                }
            }
            catch (Exception ex) {
                System.Diagnostics.Debug.WriteLine("Failed to process a link with error {0}", ex.Message);
            }
            e.Handled = true;
        }

        private void Hyperlink_RequestNavigate2(object sender, RequestNavigateEventArgs e) {
            try {
                if (this.SignInSignOutText.Text == "Sign in") {
                    ((SignOnStatusViewModel)this.DataContext).Signin();
                }
                else {
                    ((SignOnStatusViewModel)this.DataContext).Signout();
                }
            }
            catch (Exception ex) {
                System.Diagnostics.Debug.WriteLine("Failed to process a link with error {0}", ex.Message);
            }
            e.Handled = true;
        }
    }
}


text:
namespace ArduinoJson {
namespace Polyfills {

template <typename T>
inline T parseFloat(const char* s) {
  typedef TypeTraits::FloatTraits<T> traits;
  typedef typename traits::mantissa_type mantissa_t;
  typedef typename traits::exponent_type exponent_t;

  if (!s) return 0;

  bool negative_result = false;
  switch (*s) {
    case '-':
      negative_result = true;
    case '+':
      s++;
  }

  if (*s == 'n' || *s == 'N') return traits::nan();
  if (*s == 'i' || *s == 'I')
    return negative_result ? -traits::inf() : traits::inf();

  mantissa_t mantissa = 0;
  exponent_t exponent_offset = 0;

  while (isdigit(*s)) {
    if (mantissa < traits::mantissa_max / 10)
      mantissa = mantissa * 10 + (*s - '0');
    else
      exponent_offset++;
    s++;
  }

  if (*s == '.') {
    s++;
    while (isdigit(*s)) {
      if (mantissa < traits::mantissa_max / 10) {
        mantissa = mantissa * 10 + (*s - '0');
        exponent_offset--;
      }
      s++;
    }
  }

  int exponent = 0;
  if (*s == 'e' || *s == 'E') {
    s++;
    bool negative_exponent = false;
    if (*s == '-') {
      negative_exponent = true;
      s++;
    } else if (*s == '+') {
      s++;
    }

    while (isdigit(*s)) {
      exponent = exponent * 10 + (*s - '0');
      if (exponent + exponent_offset > traits::exponent_max) {
        if (negative_exponent)
          return negative_result ? -0.0f : 0.0f;
        else
          return negative_result ? -traits::inf() : traits::inf();
      }
      s++;
    }
    if (negative_exponent) exponent = -exponent;
  }
  exponent += exponent_offset;

  T result = traits::make_float(static_cast<T>(mantissa), exponent);

  return negative_result ? -result : result;
}
}
}