<commit_before>import AWS from 'aws-sdk';
import uuidv4 from 'uuid/v4';
import DynamoDBStore from '../lib/DynamoDBStore';
import { toSecondsEpoch } from '../lib/util';
import { DEFAULT_TABLE_NAME, DEFAULT_TTL } from '../lib/constants';

const TEST_OPTIONS = {
  table: {
    name: 'test-sessions',
    hashKey: 'test-sessionId',
    hashPrefix: 'test:',
  },
  dynamoConfig: {
    endpoint: process.env.AWS_DYNAMO_ENDPOINT,
  },
  touchInterval: 0,
};

const dynamoService = new AWS.DynamoDB(TEST_OPTIONS.dynamoConfig);
const documentClient = new AWS.DynamoDB.DocumentClient({
  service: dynamoService,
});

beforeAll(async () => {
  const params = {
    TableName: TEST_OPTIONS.table.name,
    KeySchema: [{ AttributeName: TEST_OPTIONS.table.hashKey, KeyType: 'HASH' }],
    AttributeDefinitions: [{ AttributeName: TEST_OPTIONS.table.hashKey, AttributeType: 'S' }],
    ProvisionedThroughput: {
      ReadCapacityUnits: 5,
      WriteCapacityUnits: 5,
    },
  };
  return dynamoService.createTable(params).promise();
});

afterAll(async () => {
  const params = {
    TableName: TEST_OPTIONS.table.name,
  };
  return dynamoService.deleteTable(params).promise();
});

describe('DynamoDBStore', () => {
  it('should create a store and a new table', () =>
    new Promise((resolve, reject) => {
      const options = {
        table: {
          name: 'test-sessions-new',
          hashKey: 'test-sessionId',
          hashPrefix: 'test:',
        },
        dynamoConfig: TEST_OPTIONS.dynamoConfig,
      };
      const store = new DynamoDBStore(options, (err) => {
        try {
          expect(err).toBeUndefined();
          resolve();
        } catch (error) {
          reject(error);
        } finally {
          const params = {
            TableName: options.table.name,
          };
          dynamoService.deleteTable(params).promise();
        }
      });
      expect(store).toBeDefined();
    }));

  it('should create a store using an existing table', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
        else resolve();
      });
      expect(store).toBeDefined();
    }));

  it('should create a store with default table values', () =>
    new Promise((resolve, reject) => {
      const options = { dynamoConfig: TEST_OPTIONS.dynamoConfig };
      const store = new DynamoDBStore(options, (err) => {
        try {
          expect(err).toBeUndefined();
          resolve();
        } catch (error) {
          reject(error);
        } finally {
          const params = {
            TableName: DEFAULT_TABLE_NAME,
          };
          dynamoService.deleteTable(params).promise();
        }
      });
      expect(store).toBeDefined();
    }));

  it('should create session with default ttl', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      const name = uuidv4();
      store.set(sessionId, { name }, async (err) => {
        try {
          if (err) reject(err);
          else {
            const params = {
              TableName: TEST_OPTIONS.table.name,
              Key: {
                [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
              },
            };
            const sessionRow = await documentClient.get(params).promise();
            expect(sessionRow).toBeDefined();
            expect(sessionRow.Item).toBeDefined();
            expect(sessionRow.Item.expires).toBeDefined();
            expect(Number.isInteger(sessionRow.Item.expires)).toBeTruthy();
            // make sure it's in the seconds epoch
            expect(String(sessionRow.Item.expires).length).toBe(
              String(toSecondsEpoch(new Date())).length,
            );
            // future date
            expect(sessionRow.Item.expires).toBeGreaterThan(toSecondsEpoch(new Date()));
            // should be before the default ttl limit
            expect(sessionRow.Item.expires).toBeLessThanOrEqual(
              toSecondsEpoch(new Date(Date.now() + DEFAULT_TTL)),
            );
            // after 10 seconds before the limit (assuming test execution time < 5 seconds)
            expect(sessionRow.Item.expires).toBeGreaterThan(
              // eslint-disable-next-line
              toSecondsEpoch(new Date(Date.now() + DEFAULT_TTL - 10000)),
            );
            expect(sessionRow.Item.sess).toBeDefined();
            expect(sessionRow.Item.sess.name).toBe(name);
            resolve();
          }
        } catch (error) {
          reject(error);
        }
      });
    }));

  it('should create session using the cookie maxAge', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      const maxAge = 100000;
      store.set(
        sessionId,
        {
          cookie: {
            maxAge,
          },
        },
        async (err) => {
          try {
            if (err) reject(err);
            else {
              const params = {
                TableName: TEST_OPTIONS.table.name,
                Key: {
                  [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
                },
              };
              const sessionRow = await documentClient.get(params).promise();
              expect(sessionRow).toBeDefined();
              expect(sessionRow.Item).toBeDefined();
              expect(sessionRow.Item.expires).toBeDefined();
              expect(Number.isInteger(sessionRow.Item.expires)).toBeTruthy();
              // make sure it's in the seconds epoch
              expect(String(sessionRow.Item.expires).length).toBe(
                String(toSecondsEpoch(new Date())).length,
              );
              // future date
              expect(sessionRow.Item.expires).toBeGreaterThan(toSecondsEpoch(new Date()));
              // should be before the default maxAge limit
              expect(sessionRow.Item.expires).toBeLessThanOrEqual(
                toSecondsEpoch(new Date(Date.now() + maxAge)),
              );
              // after 10 seconds before the limit (assuming test execution time < 5 seconds)
              expect(sessionRow.Item.expires).toBeGreaterThan(
                // eslint-disable-next-line
                toSecondsEpoch(new Date(Date.now() + maxAge - 10000)),
              );
              expect(sessionRow.Item.sess).toBeDefined();
              expect(sessionRow.Item.sess.cookie).toBeDefined();
              expect(sessionRow.Item.sess.cookie.maxAge).toBe(maxAge);
              resolve();
            }
          } catch (error) {
            reject(error);
          }
        },
      );
    }));

  it('should update a session', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = 'abcde';
      const name = uuidv4();
      store.set(sessionId, { name: uuidv4() }, async (err) => {
        if (err) reject(err);
        store.set(sessionId, { name }, async (err2) => {
          try {
            if (err2) reject(err2);
            else {
              const params = {
                TableName: TEST_OPTIONS.table.name,
                Key: {
                  [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
                },
              };
              const sessionRow = await documentClient.get(params).promise();
              expect(sessionRow.Item.sess.name).toBe(name);
              resolve();
            }
          } catch (error) {
            reject(error);
          }
        });
      });
    }));

  it('should get an existing session', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      const name = uuidv4();
      store.set(sessionId, { name }, async (err) => {
        if (err) reject(err);
        else {
          store.get(sessionId, (err2, sess) => {
            try {
              if (err2) reject(err2);
              else {
                expect(sess).toBeDefined();
                expect(sess.name).toBe(name);
                resolve();
              }
            } catch (error) {
              reject(error);
            }
          });
        }
      });
    }));

  it('should receive null for missing sessions', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      store.get(uuidv4(), (err, sess) => {
        try {
          if (err) reject(err);
          else {
            expect(sess).toBe(null);
            resolve();
          }
        } catch (error) {
          reject(error);
        }
      });
    }));

  it('should return null for expired sessions and keep the record', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(
        {
          ...TEST_OPTIONS,
          keepExpired: true,
        },
        (err) => {
          if (err) reject(err);
        },
      );
      const sessionId = uuidv4();
      store.set(
        sessionId,
        {
          cookie: {
            maxAge: -1,
          },
        },
        async (err) => {
          if (err) reject(err);
          else {
            store.get(sessionId, async (err2, sess) => {
              try {
                if (err2) reject(err2);
                else {
                  expect(sess).toBe(null);
                  const params = {
                    TableName: TEST_OPTIONS.table.name,
                    Key: {
                      [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
                    },
                  };
                  const sessionRow = await documentClient.get(params).promise();
                  expect(sessionRow).toBeDefined();
                  expect(sessionRow.Item).toBeDefined();
                  resolve();
                }
              } catch (error) {
                reject(error);
              }
            });
          }
        },
      );
    }));

  it('should return null for expired sessions and destroy the record', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      store.set(
        sessionId,
        {
          cookie: {
            maxAge: -1,
          },
        },
        async (err) => {
          if (err) reject(err);
          else {
            store.get(sessionId, async (err2, sess) => {
              try {
                if (err2) reject(err2);
                else {
                  expect(sess).toBe(null);
                  const params = {
                    TableName: TEST_OPTIONS.table.name,
                    Key: {
                      [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
                    },
                  };
                  const sessionRow = await documentClient.get(params).promise();
                  expect(sessionRow).toBeDefined();
                  expect(sessionRow.Item).toBeUndefined();
                  resolve();
                }
              } catch (error) {
                reject(error);
              }
            });
          }
        },
      );
    }));

  it('should destroy a session', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      const name = uuidv4();
      store.set(sessionId, { name }, async (err) => {
        if (err) reject(err);
        else {
          store.destroy(sessionId, (err2) => {
            if (err2) reject(err2);
            else {
              store.get(sessionId, (err3, sess) => {
                try {
                  if (err3) reject(err3);
                  else {
                    expect(sess).toBe(null);
                    resolve();
                  }
                } catch (error) {
                  reject(error);
                }
              });
            }
          });
        }
      });
    }));

  it('should touch an existing session', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      let originalExpires;
      store.set(sessionId, {}, async (err) => {
        if (err) reject(err);
        else {
          try {
            let params = {
              TableName: TEST_OPTIONS.table.name,
              Key: {
                [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
              },
            };
            let sessionRow = await documentClient.get(params).promise();
            originalExpires = sessionRow.Item.expires;
            setTimeout(() => {
              store.touch(sessionId, sessionRow.Item.sess, async (err3) => {
                if (err3) reject(err3);
                else {
                  try {
                    params = {
                      TableName: TEST_OPTIONS.table.name,
                      Key: {
                        [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table
                          .hashPrefix}${sessionId}`,
                      },
                    };
                    sessionRow = await documentClient.get(params).promise();
                    expect(sessionRow.Item.expires).toBeGreaterThan(originalExpires);
                    // 5 seconds window for test execution
                    expect(sessionRow.Item.expires).toBeLessThan(originalExpires + 5);
                    resolve();
                  } catch (err4) {
                    reject(err4);
                  }
                }
              });
            }, 2000);
          } catch (err2) {
            reject(err2);
          }
        }
      });
    }));

  it('should not touch an existing session before the interval', () =>
    new Promise((resolve, reject) => {
      const options = {
        ...TEST_OPTIONS,
        touchInterval: 30000,
      };
      const store = new DynamoDBStore(options, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      let originalExpires;
      store.set(sessionId, {}, async (err) => {
        if (err) reject(err);
        else {
          try {
            let params = {
              TableName: TEST_OPTIONS.table.name,
              Key: {
                [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
              },
            };
            let sessionRow = await documentClient.get(params).promise();
            originalExpires = sessionRow.Item.expires;
            setTimeout(() => {
              store.touch(sessionId, sessionRow.Item.sess, async (err3) => {
                if (err3) reject(err3);
                else {
                  try {
                    params = {
                      TableName: TEST_OPTIONS.table.name,
                      Key: {
                        [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table
                          .hashPrefix}${sessionId}`,
                      },
                    };
                    sessionRow = await documentClient.get(params).promise();
                    expect(sessionRow.Item.expires).toBe(originalExpires);
                    resolve();
                  } catch (err4) {
                    reject(err4);
                  }
                }
              });
            }, 2000);
          } catch (err2) {
            reject(err2);
          }
        }
      });
    }));
});
<commit_msg>Fixed hanging test when dynamodb is down<commit_after>import AWS from 'aws-sdk';
import uuidv4 from 'uuid/v4';
import DynamoDBStore from '../lib/DynamoDBStore';
import { toSecondsEpoch } from '../lib/util';
import { DEFAULT_TABLE_NAME, DEFAULT_TTL } from '../lib/constants';

const TEST_OPTIONS = {
  table: {
    name: 'test-sessions',
    hashKey: 'test-sessionId',
    hashPrefix: 'test:',
  },
  dynamoConfig: {
    endpoint: process.env.AWS_DYNAMO_ENDPOINT,
    maxRetries: 0,
    httpOptions: {
      connectTimeout: 1000,
    },
  },
  touchInterval: 0,
};

const dynamoService = new AWS.DynamoDB(TEST_OPTIONS.dynamoConfig);
const documentClient = new AWS.DynamoDB.DocumentClient({
  service: dynamoService,
});

beforeAll(async () => {
  const params = {
    TableName: TEST_OPTIONS.table.name,
    KeySchema: [{ AttributeName: TEST_OPTIONS.table.hashKey, KeyType: 'HASH' }],
    AttributeDefinitions: [{ AttributeName: TEST_OPTIONS.table.hashKey, AttributeType: 'S' }],
    ProvisionedThroughput: {
      ReadCapacityUnits: 5,
      WriteCapacityUnits: 5,
    },
  };
  return dynamoService.createTable(params).promise();
});

afterAll(async () => {
  const params = {
    TableName: TEST_OPTIONS.table.name,
  };
  return dynamoService.deleteTable(params).promise();
});

describe('DynamoDBStore', () => {
  it('should create a store and a new table', () =>
    new Promise((resolve, reject) => {
      const options = {
        table: {
          name: 'test-sessions-new',
          hashKey: 'test-sessionId',
          hashPrefix: 'test:',
        },
        dynamoConfig: TEST_OPTIONS.dynamoConfig,
      };
      const store = new DynamoDBStore(options, (err) => {
        try {
          expect(err).toBeUndefined();
          resolve();
        } catch (error) {
          reject(error);
        } finally {
          const params = {
            TableName: options.table.name,
          };
          dynamoService.deleteTable(params).promise();
        }
      });
      expect(store).toBeDefined();
    }));

  it('should create a store using an existing table', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
        else resolve();
      });
      expect(store).toBeDefined();
    }));

  it('should create a store with default table values', () =>
    new Promise((resolve, reject) => {
      const options = { dynamoConfig: TEST_OPTIONS.dynamoConfig };
      const store = new DynamoDBStore(options, (err) => {
        try {
          expect(err).toBeUndefined();
          resolve();
        } catch (error) {
          reject(error);
        } finally {
          const params = {
            TableName: DEFAULT_TABLE_NAME,
          };
          dynamoService.deleteTable(params).promise();
        }
      });
      expect(store).toBeDefined();
    }));

  it('should create session with default ttl', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      const name = uuidv4();
      store.set(sessionId, { name }, async (err) => {
        try {
          if (err) reject(err);
          else {
            const params = {
              TableName: TEST_OPTIONS.table.name,
              Key: {
                [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
              },
            };
            const sessionRow = await documentClient.get(params).promise();
            expect(sessionRow).toBeDefined();
            expect(sessionRow.Item).toBeDefined();
            expect(sessionRow.Item.expires).toBeDefined();
            expect(Number.isInteger(sessionRow.Item.expires)).toBeTruthy();
            // make sure it's in the seconds epoch
            expect(String(sessionRow.Item.expires).length).toBe(
              String(toSecondsEpoch(new Date())).length,
            );
            // future date
            expect(sessionRow.Item.expires).toBeGreaterThan(toSecondsEpoch(new Date()));
            // should be before the default ttl limit
            expect(sessionRow.Item.expires).toBeLessThanOrEqual(
              toSecondsEpoch(new Date(Date.now() + DEFAULT_TTL)),
            );
            // after 10 seconds before the limit (assuming test execution time < 5 seconds)
            expect(sessionRow.Item.expires).toBeGreaterThan(
              // eslint-disable-next-line
              toSecondsEpoch(new Date(Date.now() + DEFAULT_TTL - 10000)),
            );
            expect(sessionRow.Item.sess).toBeDefined();
            expect(sessionRow.Item.sess.name).toBe(name);
            resolve();
          }
        } catch (error) {
          reject(error);
        }
      });
    }));

  it('should create session using the cookie maxAge', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      const maxAge = 100000;
      store.set(
        sessionId,
        {
          cookie: {
            maxAge,
          },
        },
        async (err) => {
          try {
            if (err) reject(err);
            else {
              const params = {
                TableName: TEST_OPTIONS.table.name,
                Key: {
                  [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
                },
              };
              const sessionRow = await documentClient.get(params).promise();
              expect(sessionRow).toBeDefined();
              expect(sessionRow.Item).toBeDefined();
              expect(sessionRow.Item.expires).toBeDefined();
              expect(Number.isInteger(sessionRow.Item.expires)).toBeTruthy();
              // make sure it's in the seconds epoch
              expect(String(sessionRow.Item.expires).length).toBe(
                String(toSecondsEpoch(new Date())).length,
              );
              // future date
              expect(sessionRow.Item.expires).toBeGreaterThan(toSecondsEpoch(new Date()));
              // should be before the default maxAge limit
              expect(sessionRow.Item.expires).toBeLessThanOrEqual(
                toSecondsEpoch(new Date(Date.now() + maxAge)),
              );
              // after 10 seconds before the limit (assuming test execution time < 5 seconds)
              expect(sessionRow.Item.expires).toBeGreaterThan(
                // eslint-disable-next-line
                toSecondsEpoch(new Date(Date.now() + maxAge - 10000)),
              );
              expect(sessionRow.Item.sess).toBeDefined();
              expect(sessionRow.Item.sess.cookie).toBeDefined();
              expect(sessionRow.Item.sess.cookie.maxAge).toBe(maxAge);
              resolve();
            }
          } catch (error) {
            reject(error);
          }
        },
      );
    }));

  it('should update a session', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = 'abcde';
      const name = uuidv4();
      store.set(sessionId, { name: uuidv4() }, async (err) => {
        if (err) reject(err);
        store.set(sessionId, { name }, async (err2) => {
          try {
            if (err2) reject(err2);
            else {
              const params = {
                TableName: TEST_OPTIONS.table.name,
                Key: {
                  [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
                },
              };
              const sessionRow = await documentClient.get(params).promise();
              expect(sessionRow.Item.sess.name).toBe(name);
              resolve();
            }
          } catch (error) {
            reject(error);
          }
        });
      });
    }));

  it('should get an existing session', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      const name = uuidv4();
      store.set(sessionId, { name }, async (err) => {
        if (err) reject(err);
        else {
          store.get(sessionId, (err2, sess) => {
            try {
              if (err2) reject(err2);
              else {
                expect(sess).toBeDefined();
                expect(sess.name).toBe(name);
                resolve();
              }
            } catch (error) {
              reject(error);
            }
          });
        }
      });
    }));

  it('should receive null for missing sessions', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      store.get(uuidv4(), (err, sess) => {
        try {
          if (err) reject(err);
          else {
            expect(sess).toBe(null);
            resolve();
          }
        } catch (error) {
          reject(error);
        }
      });
    }));

  it('should return null for expired sessions and keep the record', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(
        {
          ...TEST_OPTIONS,
          keepExpired: true,
        },
        (err) => {
          if (err) reject(err);
        },
      );
      const sessionId = uuidv4();
      store.set(
        sessionId,
        {
          cookie: {
            maxAge: -1,
          },
        },
        async (err) => {
          if (err) reject(err);
          else {
            store.get(sessionId, async (err2, sess) => {
              try {
                if (err2) reject(err2);
                else {
                  expect(sess).toBe(null);
                  const params = {
                    TableName: TEST_OPTIONS.table.name,
                    Key: {
                      [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
                    },
                  };
                  const sessionRow = await documentClient.get(params).promise();
                  expect(sessionRow).toBeDefined();
                  expect(sessionRow.Item).toBeDefined();
                  resolve();
                }
              } catch (error) {
                reject(error);
              }
            });
          }
        },
      );
    }));

  it('should return null for expired sessions and destroy the record', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      store.set(
        sessionId,
        {
          cookie: {
            maxAge: -1,
          },
        },
        async (err) => {
          if (err) reject(err);
          else {
            store.get(sessionId, async (err2, sess) => {
              try {
                if (err2) reject(err2);
                else {
                  expect(sess).toBe(null);
                  const params = {
                    TableName: TEST_OPTIONS.table.name,
                    Key: {
                      [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
                    },
                  };
                  const sessionRow = await documentClient.get(params).promise();
                  expect(sessionRow).toBeDefined();
                  expect(sessionRow.Item).toBeUndefined();
                  resolve();
                }
              } catch (error) {
                reject(error);
              }
            });
          }
        },
      );
    }));

  it('should destroy a session', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      const name = uuidv4();
      store.set(sessionId, { name }, async (err) => {
        if (err) reject(err);
        else {
          store.destroy(sessionId, (err2) => {
            if (err2) reject(err2);
            else {
              store.get(sessionId, (err3, sess) => {
                try {
                  if (err3) reject(err3);
                  else {
                    expect(sess).toBe(null);
                    resolve();
                  }
                } catch (error) {
                  reject(error);
                }
              });
            }
          });
        }
      });
    }));

  it('should touch an existing session', () =>
    new Promise((resolve, reject) => {
      const store = new DynamoDBStore(TEST_OPTIONS, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      let originalExpires;
      store.set(sessionId, {}, async (err) => {
        if (err) reject(err);
        else {
          try {
            let params = {
              TableName: TEST_OPTIONS.table.name,
              Key: {
                [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
              },
            };
            let sessionRow = await documentClient.get(params).promise();
            originalExpires = sessionRow.Item.expires;
            setTimeout(() => {
              store.touch(sessionId, sessionRow.Item.sess, async (err3) => {
                if (err3) reject(err3);
                else {
                  try {
                    params = {
                      TableName: TEST_OPTIONS.table.name,
                      Key: {
                        [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table
                          .hashPrefix}${sessionId}`,
                      },
                    };
                    sessionRow = await documentClient.get(params).promise();
                    expect(sessionRow.Item.expires).toBeGreaterThan(originalExpires);
                    // 5 seconds window for test execution
                    expect(sessionRow.Item.expires).toBeLessThan(originalExpires + 5);
                    resolve();
                  } catch (err4) {
                    reject(err4);
                  }
                }
              });
            }, 2000);
          } catch (err2) {
            reject(err2);
          }
        }
      });
    }));

  it('should not touch an existing session before the interval', () =>
    new Promise((resolve, reject) => {
      const options = {
        ...TEST_OPTIONS,
        touchInterval: 30000,
      };
      const store = new DynamoDBStore(options, (err) => {
        if (err) reject(err);
      });
      const sessionId = uuidv4();
      let originalExpires;
      store.set(sessionId, {}, async (err) => {
        if (err) reject(err);
        else {
          try {
            let params = {
              TableName: TEST_OPTIONS.table.name,
              Key: {
                [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table.hashPrefix}${sessionId}`,
              },
            };
            let sessionRow = await documentClient.get(params).promise();
            originalExpires = sessionRow.Item.expires;
            setTimeout(() => {
              store.touch(sessionId, sessionRow.Item.sess, async (err3) => {
                if (err3) reject(err3);
                else {
                  try {
                    params = {
                      TableName: TEST_OPTIONS.table.name,
                      Key: {
                        [TEST_OPTIONS.table.hashKey]: `${TEST_OPTIONS.table
                          .hashPrefix}${sessionId}`,
                      },
                    };
                    sessionRow = await documentClient.get(params).promise();
                    expect(sessionRow.Item.expires).toBe(originalExpires);
                    resolve();
                  } catch (err4) {
                    reject(err4);
                  }
                }
              });
            }, 2000);
          } catch (err2) {
            reject(err2);
          }
        }
      });
    }));
});
<|endoftext|>


<commit_before>import { PriorityQueue } from '../src/utilities/PriorityQueue.js';

const nextFrame = () => new Promise( resolve => requestAnimationFrame( resolve ) );
const nextTick = () => new Promise( resolve => process.nextTick( resolve ) );

describe( 'PriorityQueue', () => {

	it( 'should run jobs automatically in the correct order.', async () => {

		const queue = new PriorityQueue();
		queue.maxJobs = 6;
		queue.priorityCallback = item => item.priority;
		queue.add( { priority : 6 }, () => new Promise( () => {} ) );
		queue.add( { priority : 3 }, () => new Promise( () => {} ) );
		queue.add( { priority : 4 }, () => new Promise( () => {} ) );
		queue.add( { priority : 0 }, () => new Promise( () => {} ) );
		queue.add( { priority : 8 }, () => new Promise( () => {} ) );
		queue.add( { priority : 2 }, () => new Promise( () => {} ) );
		queue.add( { priority : 1 }, () => new Promise( () => {} ) );

		await nextFrame();

		expect( queue.items.map( item => item.priority ) ).toEqual( [ 0 ] );
		expect( queue.currJobs ).toEqual( 6 );

	} );

	it( 'should run jobs in the correct order.', async () => {

		const result = [];
		const cb = item => new Promise( resolve => {

			result.push( item.priority );
			resolve();

		} );

		const queue = new PriorityQueue();
		queue.maxJobs = 1;
		queue.priorityCallback = item => item.priority;
		queue.add( { priority : 6 }, cb );
		queue.add( { priority : 3 }, cb );
		queue.add( { priority : 4 }, cb );
		queue.add( { priority : 0 }, cb );
		queue.add( { priority : 8 }, cb );
		queue.add( { priority : 2 }, cb );
		queue.add( { priority : 1 }, cb );
		expect( queue.items.length ).toEqual( queue.callbacks.size );

		await nextTick();

		expect( result ).toEqual( [ 8, 6, 4, 3, 2, 1, 0 ] );
		expect( queue.items.length ).toEqual( queue.callbacks.size );

	} );

	it( 'should remove an item from the queue correctly.', () => {

		const A = { priority : 0 };
		const B = { priority : 1 };
		const C = { priority : 2 };
		const D = { priority : 3 };
		const queue = new PriorityQueue();
		queue.priorityCallback = item => item.priority;
		queue.add( A, () => new Promise( () => {} ) );
		queue.add( B, () => new Promise( () => {} ) );
		queue.add( C, () => new Promise( () => {} ) );
		queue.add( D, () => new Promise( () => {} ) );
		queue.sort();

		expect( queue.items ).toEqual( [ A, B, C, D ] );

		queue.remove( C );
		expect( queue.items ).toEqual( [ A, B, D ] );

		queue.remove( A );
		expect( queue.items ).toEqual( [ B, D ] );

		queue.remove( B );
		expect( queue.items ).toEqual( [ D ] );

		queue.remove( D );
		expect( queue.items ).toEqual( [] );
		expect( queue.items.length ).toEqual( queue.callbacks.size );

	} );

	it( 'should automatically run new jobs when one is finished.', async () => {

		let called = 0;
		let resolveFunc = null;
		const queue = new PriorityQueue();
		queue.maxJobs = 1;
		queue.priorityCallback = item => item.priority;

		queue.add( { priority : 1 }, () => new Promise( resolve => {

			resolveFunc = resolve;
			called ++;

		} ) );

		queue.add( { priority : 0 }, () => new Promise( () => {

			called ++;

		} ) );

		expect( queue.currJobs ).toEqual( 0 );

		await nextFrame();

		expect( queue.currJobs ).toEqual( 1 );
		expect( resolveFunc ).not.toEqual( null );
		expect( called ).toEqual( 1 );

		resolveFunc();
		await nextFrame();

		expect( queue.currJobs ).toEqual( 1 );
		expect( called ).toEqual( 2 );

	} );

	it( 'should fire the callback with the item and priority.', async () => {

		const A = { priority : 100 };
		const queue = new PriorityQueue();
		queue.priorityCallback = item => item.priority;

		queue.add( A, item => new Promise( () => {

			expect( item ).toEqual( A );

		} ) );

		await nextFrame();

	} );

	it( 'should return a promise that resolves from the add function.',  async () => {

		const queue = new PriorityQueue();
		queue.priorityCallback = item => item.priority;

		let result = null;
		queue.add( { priority : 0 }, item => Promise.resolve( 1000 ) ).then( res => result = res );

		expect( result ).toEqual( null );

		await nextFrame();

		expect( result ).toEqual( 1000 );

	} );

} );
<commit_msg>Add test for autoUpdate<commit_after>import { PriorityQueue } from '../src/utilities/PriorityQueue.js';

const nextFrame = () => new Promise( resolve => requestAnimationFrame( resolve ) );
const nextTick = () => new Promise( resolve => process.nextTick( resolve ) );

describe( 'PriorityQueue', () => {

	it( 'should run jobs automatically in the correct order.', async () => {

		const queue = new PriorityQueue();
		queue.maxJobs = 6;
		queue.priorityCallback = item => item.priority;
		queue.add( { priority : 6 }, () => new Promise( () => {} ) );
		queue.add( { priority : 3 }, () => new Promise( () => {} ) );
		queue.add( { priority : 4 }, () => new Promise( () => {} ) );
		queue.add( { priority : 0 }, () => new Promise( () => {} ) );
		queue.add( { priority : 8 }, () => new Promise( () => {} ) );
		queue.add( { priority : 2 }, () => new Promise( () => {} ) );
		queue.add( { priority : 1 }, () => new Promise( () => {} ) );

		await nextFrame();

		expect( queue.items.map( item => item.priority ) ).toEqual( [ 0 ] );
		expect( queue.currJobs ).toEqual( 6 );

	} );

	it( 'should run jobs in the correct order.', async () => {

		const result = [];
		const cb = item => new Promise( resolve => {

			result.push( item.priority );
			resolve();

		} );

		const queue = new PriorityQueue();
		queue.maxJobs = 1;
		queue.priorityCallback = item => item.priority;
		queue.add( { priority : 6 }, cb );
		queue.add( { priority : 3 }, cb );
		queue.add( { priority : 4 }, cb );
		queue.add( { priority : 0 }, cb );
		queue.add( { priority : 8 }, cb );
		queue.add( { priority : 2 }, cb );
		queue.add( { priority : 1 }, cb );
		expect( queue.items.length ).toEqual( queue.callbacks.size );

		await nextTick();

		expect( result ).toEqual( [ 8, 6, 4, 3, 2, 1, 0 ] );
		expect( queue.items.length ).toEqual( queue.callbacks.size );

	} );

	it( 'should remove an item from the queue correctly.', () => {

		const A = { priority : 0 };
		const B = { priority : 1 };
		const C = { priority : 2 };
		const D = { priority : 3 };
		const queue = new PriorityQueue();
		queue.priorityCallback = item => item.priority;
		queue.add( A, () => new Promise( () => {} ) );
		queue.add( B, () => new Promise( () => {} ) );
		queue.add( C, () => new Promise( () => {} ) );
		queue.add( D, () => new Promise( () => {} ) );
		queue.sort();

		expect( queue.items ).toEqual( [ A, B, C, D ] );

		queue.remove( C );
		expect( queue.items ).toEqual( [ A, B, D ] );

		queue.remove( A );
		expect( queue.items ).toEqual( [ B, D ] );

		queue.remove( B );
		expect( queue.items ).toEqual( [ D ] );

		queue.remove( D );
		expect( queue.items ).toEqual( [] );
		expect( queue.items.length ).toEqual( queue.callbacks.size );

	} );

	it( 'should automatically run new jobs when one is finished.', async () => {

		let called = 0;
		let resolveFunc = null;
		const queue = new PriorityQueue();
		queue.maxJobs = 1;
		queue.priorityCallback = item => item.priority;

		queue.add( { priority : 1 }, () => new Promise( resolve => {

			resolveFunc = resolve;
			called ++;

		} ) );

		queue.add( { priority : 0 }, () => new Promise( () => {

			called ++;

		} ) );

		expect( queue.currJobs ).toEqual( 0 );

		await nextFrame();

		expect( queue.currJobs ).toEqual( 1 );
		expect( resolveFunc ).not.toEqual( null );
		expect( called ).toEqual( 1 );

		resolveFunc();
		await nextFrame();

		expect( queue.currJobs ).toEqual( 1 );
		expect( called ).toEqual( 2 );

	} );

	it( 'should fire the callback with the item and priority.', async () => {

		const A = { priority : 100 };
		const queue = new PriorityQueue();
		queue.priorityCallback = item => item.priority;

		queue.add( A, item => new Promise( () => {

			expect( item ).toEqual( A );

		} ) );

		await nextFrame();

	} );

	it( 'should return a promise that resolves from the add function.',  async () => {

		const queue = new PriorityQueue();
		queue.priorityCallback = item => item.priority;

		let result = null;
		queue.add( { priority : 0 }, item => Promise.resolve( 1000 ) ).then( res => result = res );

		expect( result ).toEqual( null );

		await nextFrame();

		expect( result ).toEqual( 1000 );

	} );

	it( 'should not automatically run if autoUpdate is false.', async () => {

		const queue = new PriorityQueue();
		queue.priorityCallback = () => 0;
		queue.autoUpdate = false;
		queue.maxJobs = 1;

		queue.add( {}, async () => {} );
		queue.add( {}, async () => {} );

		expect( queue.items ).toHaveLength( 2 );

		await nextTick();

		expect( queue.items ).toHaveLength( 2 );

		queue.scheduleJobRun();
		await nextTick();

		expect( queue.items ).toHaveLength( 1 );

		await nextTick();

		expect( queue.items ).toHaveLength( 1 );

		queue.scheduleJobRun();
		await nextTick();

		expect( queue.items ).toHaveLength( 0 );

	});

} );
<|endoftext|>


<commit_before>var Sequelize = require(__dirname + "/../lib/sequelize/Sequelize").Sequelize
var s = new Sequelize('sequelize_test', 'test', 'test', {disableLogging: true})
var Day = s.define('Day', { name: Sequelize.TEXT })

module.exports = {
  'constructor': function(assert) {
    assert.eql(Day.associations, [])
    assert.eql(Day.attributes, {"name": {type: "TEXT"},"createdAt": {type: "DATETIME", allowNull: false},"updatedAt": {type: "DATETIME", allowNull: false}})
    assert.eql(Day.tableName, 'Days')
  },
  'new': function(assert) {
    var day = new Day({name: 'asd'})
    assert.isNull(day.id)
    assert.eql(day.table, Day)
    assert.eql(day.name, 'asd')
    assert.isUndefined(new Day({name: 'asd', bla: 'foo'}).bla)
  },
  'prepareAssociations belongsTo': function(assert) {
    var s = new Sequelize('sequelize_test', 'test', 'test', {disableLogging: true})
    var Me = s.define('Me', {})
    var You = s.define('You', {})
    var assoc = Me.hasOne('you', You)
    You.belongsTo('me', Me, assoc)

    Me.prepareAssociations()
    You.prepareAssociations()

    assert.includes(Sequelize.Helper.Hash.keys(You.attributes), 'meId')
    assert.isDefined(You.attributes.meId)
    assert.isNotNull(You.attributes.meId)
  },
  'prepareAssociations hasMany': function(assert) {
    var House = s.define('House', {})
    var Person = s.define('Person', {})

    House.hasMany('members', Person, 'households')
    House.prepareAssociations()
    Person.prepareAssociations()

    assert.isUndefined(House.attributes.personId)
    assert.isUndefined(House.attributes.membersId)
    assert.isUndefined(Person.attributes.houseId)

    assert.isDefined(s.tables.HouseholdsMembers)
  },
  'sync should return the table class': function(assert, beforeExit) {
    var toBeTested = null
    Day.sync(function(_Day) { toBeTested = _Day })
    beforeExit(function() { assert.eql(toBeTested, Day) })
  },
  'drop should return the table class': function(assert, beforeExit) {
    var toBeTested = null
    Day.drop(function(_Day) { toBeTested = _Day })
    beforeExit(function() { assert.eql(toBeTested, Day) })
  },
  'findAll should return all items as class objects': function(assert, beforeExit) {
    var allFindAllTestItems = null
    var FindAllTest = s.define('FindAllTest', {})

    Sequelize.chainQueries([
      {drop: FindAllTest}, {sync: FindAllTest}, {save: new FindAllTest({})}, {save: new FindAllTest({})}
    ], function() {
      FindAllTest.findAll(function(findAlls) {
        allFindAllTestItems = findAlls
      })
    })

    beforeExit(function(){
      assert.equal(allFindAllTestItems.length, 2)
      allFindAllTestItems.forEach(function(item) {
        assert.equal(item instanceof FindAllTest, true)
      })
    })
  },
  'find returns the correct item': function(assert, beforeExit) {
    var item = null
    var itemToMatch = null
    var FindTest = s.define('FindTest', { name: Sequelize.STRING })
    FindTest.drop(function() {
      FindTest.sync(function() {
        itemToMatch = new FindTest({name: 'foo'})
        itemToMatch.save(function() {
          new FindTest({name: 'bar'}).save(function() {
            FindTest.find({name: 'foo'}, function(result) {
              item = result
            })
          })
        })
      })
    })

    beforeExit(function() {
      assert.equal(itemToMatch.id, item.id)
      assert.equal(itemToMatch.name, item.name)
    })
  },
  'find returns data in correct attributes': function(assert, beforeExit) {
    var assertMe = null
    var FindMeNow = s.define('FindMeNow', { title: Sequelize.STRING, content: Sequelize.TEXT })
    FindMeNow.drop(function() {
      FindMeNow.sync(function() {
        new FindMeNow({title: 'a title', content: 'a content'}).save(function(blubb) {
          assertMe = blubb
        })
      })
    })
    beforeExit(function() {
      assert.isNotNull(assertMe)
      assert.equal(assertMe.title, 'a title')
      assert.equal(assertMe.content, 'a content')
    })
  },
  'sqlResultToObject returns the correct object': function(assert) {
    var SqlResultToObjectTest = s.define('SqlResultToObject', {name: Sequelize.STRING})
    var toBeTested = SqlResultToObjectTest.sqlResultToObject({
      id: 1,
      name: 'foo'
    })
    assert.equal(toBeTested instanceof SqlResultToObjectTest, true)
    assert.equal(toBeTested.id, 1)
    assert.equal(toBeTested.name, 'foo')
  },
  'hasMany': function(assert) {
    var HasManyBlubb = s.define('HasManyBlubb', {})
    Day.hasMany('HasManyBlubbs', HasManyBlubb)
    assert.isDefined(new Day({name:''}).getHasManyBlubbs)
  },
  'hasMany: set association': function(assert, beforeExit) {
    var assoc = null
    var Character = s.define('Character', {})
    var Word = s.define('Word', {})
    Character.hasMany('Words', Word, 'Characters')

    Sequelize.chainQueries([
      {drop: Character}, {drop: Word}, {prepareAssociations: Word}, {prepareAssociations: Character}, {sync: Word}, {sync: Character}
    ],
    function() {
      var Association = s.tables.CharactersWords.klass
      Association.sync(function() {
        var w = new Word()
        var c1 = new Character()
        var c2 = new Character()
        Sequelize.chainQueries([{save: w}, {save: c1}, {save: c2}], function() {
          w.setCharacters([c1, c2], function(associations) {
            assoc = associations
          })
        })
      })
    })

    beforeExit(function() {
      assert.isNotNull(assoc)
      assert.equal(assoc.length, 2)
    })
  },
  'hasOne': function(assert) {
    var s = new Sequelize('sequelize_test', 'test', 'test', {disableLogging: true})
    var Day = s.define('Day2', { name: Sequelize.TEXT })
    var HasOneBlubb = s.define('HasOneBlubb', {})
    Day.hasOne('HasOneBlubb', HasOneBlubb)
    assert.isDefined(new Day({name:''}).getHasOneBlubb)
  },
  'hasOne set association': function(assert, beforeExit) {
    var s2 = new Sequelize('sequelize_test', 'test', 'test', {disableLogging: true})
    var Task = s2.define('Task', {title: Sequelize.STRING})
    var Deadline = s2.define('Deadline', {date: Sequelize.DATE})

    var taskAssoc = Task.hasOne('deadline', Deadline)
    Deadline.belongsTo('task', Task, taskAssoc)

    var task = new Task({ title:'do smth' })
    var deadline = new Deadline({date: new Date()})
    var assertMe = null

    Sequelize.chainQueries([{sync: s2}, {drop: s2}, {sync: s2}, {save: task}, {save: deadline}], function() {
      task.setDeadline(deadline, function(_deadline) {
        assertMe = _deadline
      })
    })

    beforeExit(function() {
      assert.isNotNull(assertMe)
      assert.eql(assertMe, deadline)
    })
  },
  'belongsTo': function(assert) {
    var BelongsToBlubb = s.define('BelongsToBlubb', {})
    var Day = s.define('Day2', { name: Sequelize.TEXT })
    var assoc = Day.hasOne('asd', BelongsToBlubb)
    Day.belongsTo('BelongsToBlubb', BelongsToBlubb, assoc)
    assert.isDefined(new Day({name:''}).getBelongsToBlubb)
  },
  'belongsTo: set association': function(assert, beforeExit) {
    var s2 = new Sequelize('sequelize_test', 'test', 'test', {disableLogging: true})
    var Task = s2.define('Task', {title: Sequelize.STRING})
    var Deadline = s2.define('Deadline', {date: Sequelize.DATE})
    var allowExit = false

    var assoc = Task.hasOne('deadline', Deadline)
    Deadline.belongsTo('task', Task, assoc)

    var task = new Task({ title:'do smth' })
    var deadline = new Deadline({date: new Date()})

    Sequelize.chainQueries([{drop: s2}, {sync: s2}], function() {
      task.save(function() {
        deadline.save(function() {
          assert.isDefined(deadline.id)
          assert.isNotNull(deadline.id)
          deadline.setTask(task, function(_task) {
            assert.isNotNull(_task)
            assert.eql(_task.id, task.id)
            allowExit = true
          })
        })
      })
    })

    beforeExit(function() {
      assert.equal(allowExit, true)
    })
  },
  'identifier': function(assert) {
    assert.equal(s.define('Identifier', {}).identifier, 'identifierId')
  },
  'values': function(assert) {
    var day = new Day({name: 's'})
    assert.eql(day.values, { name: "s", createdAt: null, updatedAt: null})
  },
  'save should set the id of a newly created object': function(assert, beforeExit) {
    var subject = null
    var SaveTest = s.define('SaveTest', {name: Sequelize.STRING})
    SaveTest.drop(function(){
      SaveTest.sync(function() {
         new SaveTest({name:'s'}).save(function(_saveTest){
          subject = _saveTest
        })
      })
    })

    beforeExit(function() {
      assert.isNotNull(subject.id)
    })
  },
  'updateAttributes should update available attributes': function(assert, beforeExit) {
    var subject = null
    var UpdateMe = s.define('UpdateMe', {name: Sequelize.STRING})
    Sequelize.chainQueries([{drop: UpdateMe}, {sync: UpdateMe}], function() {
      new UpdateMe({name:'Monday'}).save(function(u) {
        u.updateAttributes({name: 'Sunday', foo: 'bar'}, function(u) {
          subject = u
        })
      })
    })

    beforeExit(function() {
      assert.isNotNull(subject)
      assert.equal(subject.name, 'Sunday')
      assert.isUndefined(subject.foo)
    })
  },
  'destroy should make the object unavailable': function(assert, beforeExit) {
    var subject = 1
    var UpdateAttributesTest = s.define('UpdateAttributeTest', {name: Sequelize.STRING})
    Sequelize.chainQueries([{drop: UpdateAttributesTest}, {sync: UpdateAttributesTest}], function() {
      new UpdateAttributesTest({name:'Monday'}).save(function(day) {
        day.destroy(function() {
          UpdateAttributesTest.find(day.id, function(result) {
            subject = result
          })
        })
      })
    })
    beforeExit(function() {
      assert.isNull(subject)
    })
  },
  'boolean ==> save': function(assert, beforeExit) {
    var BooleanTest = s.define("BooleanTest", {flag: Sequelize.BOOLEAN})
    var testIsFinished = false
    BooleanTest.sync(function() {
      new BooleanTest({flag: true}).save(function(obj) {
        assert.equal(obj.flag, true)
        obj.updateAttributes({flag: false}, function(obj2) {
          assert.equal(obj2.flag, false)
          testIsFinished = true
        })
      })
    })
    beforeExit(function() { assert.equal(true, testIsFinished) })
  },
  'sync ==> failure': function(assert, beforeExit) {
    var testIsFinished = false,
        sequelizeWithInvalidCredentials = new Sequelize('foo', 'bar', 'barfoos'),
        Fail = sequelizeWithInvalidCredentials.define('Fail', {})

    Fail.sync(function(table, err) {
      assert.isDefined(err)
      assert.isDefined(err.message)
      testIsFinished = true
    })
    beforeExit(function() { assert.equal(testIsFinished, true) })
  },
  'drop ==> failure': function(assert, beforeExit) {
    var testIsFinished = false,
        sequelizeWithInvalidCredentials = new Sequelize('foo', 'bar', 'barfoos'),
        Fail = sequelizeWithInvalidCredentials.define('Fail', {})

    Fail.drop(function(table, err) {
      assert.isDefined(err)
      assert.isDefined(err.message)
      testIsFinished = true
    })
    beforeExit(function() { assert.equal(testIsFinished, true) })
  },
  'default values': function(assert) {
    var DefaultTest = s.define("DefaultTest", {
      aString: { type: Sequelize.STRING, allowNull: false, default: 'woot'},
      aNumber: { type: Sequelize.INTEGER, allowNull: true},
      aBoolean: { type: Sequelize.BOOLEAN, allowNull: false, default: false},
      aText: { type: Sequelize.TEXT, allowNull: true }
    }),
    instance = new DefaultTest({})

    assert.eql(instance.aString, 'woot')
    assert.isUndefined(instance.aNumber)
    assert.eql(instance.aBoolean, false)
    assert.isUndefined(instance.aText)
  }
}<commit_msg>fixed specs to use root with no password<commit_after>var Sequelize = require(__dirname + "/../lib/sequelize/Sequelize").Sequelize
var s = new Sequelize('sequelize_test', 'root', null, {disableLogging: true})
var Day = s.define('Day', { name: Sequelize.TEXT })

module.exports = {
  'constructor': function(assert) {
    assert.eql(Day.associations, [])
    assert.eql(Day.attributes, {"name": {type: "TEXT"},"createdAt": {type: "DATETIME", allowNull: false},"updatedAt": {type: "DATETIME", allowNull: false}})
    assert.eql(Day.tableName, 'Days')
  },
  'new': function(assert) {
    var day = new Day({name: 'asd'})
    assert.isNull(day.id)
    assert.eql(day.table, Day)
    assert.eql(day.name, 'asd')
    assert.isUndefined(new Day({name: 'asd', bla: 'foo'}).bla)
  },
  'prepareAssociations belongsTo': function(assert) {
    var s = new Sequelize('sequelize_test', 'root', null, {disableLogging: true})
    var Me = s.define('Me', {})
    var You = s.define('You', {})
    var assoc = Me.hasOne('you', You)
    You.belongsTo('me', Me, assoc)

    Me.prepareAssociations()
    You.prepareAssociations()

    assert.includes(Sequelize.Helper.Hash.keys(You.attributes), 'meId')
    assert.isDefined(You.attributes.meId)
    assert.isNotNull(You.attributes.meId)
  },
  'prepareAssociations hasMany': function(assert) {
    var House = s.define('House', {})
    var Person = s.define('Person', {})

    House.hasMany('members', Person, 'households')
    House.prepareAssociations()
    Person.prepareAssociations()

    assert.isUndefined(House.attributes.personId)
    assert.isUndefined(House.attributes.membersId)
    assert.isUndefined(Person.attributes.houseId)

    assert.isDefined(s.tables.HouseholdsMembers)
  },
  'sync should return the table class': function(assert, beforeExit) {
    var toBeTested = null
    Day.sync(function(_Day) { toBeTested = _Day })
    beforeExit(function() { assert.eql(toBeTested, Day) })
  },
  'drop should return the table class': function(assert, beforeExit) {
    var toBeTested = null
    Day.drop(function(_Day) { toBeTested = _Day })
    beforeExit(function() { assert.eql(toBeTested, Day) })
  },
  'findAll should return all items as class objects': function(assert, beforeExit) {
    var allFindAllTestItems = null
    var FindAllTest = s.define('FindAllTest', {})

    Sequelize.chainQueries([
      {drop: FindAllTest}, {sync: FindAllTest}, {save: new FindAllTest({})}, {save: new FindAllTest({})}
    ], function() {
      FindAllTest.findAll(function(findAlls) {
        allFindAllTestItems = findAlls
      })
    })

    beforeExit(function(){
      assert.equal(allFindAllTestItems.length, 2)
      allFindAllTestItems.forEach(function(item) {
        assert.equal(item instanceof FindAllTest, true)
      })
    })
  },
  'find returns the correct item': function(assert, beforeExit) {
    var item = null
    var itemToMatch = null
    var FindTest = s.define('FindTest', { name: Sequelize.STRING })
    FindTest.drop(function() {
      FindTest.sync(function() {
        itemToMatch = new FindTest({name: 'foo'})
        itemToMatch.save(function() {
          new FindTest({name: 'bar'}).save(function() {
            FindTest.find({name: 'foo'}, function(result) {
              item = result
            })
          })
        })
      })
    })

    beforeExit(function() {
      assert.equal(itemToMatch.id, item.id)
      assert.equal(itemToMatch.name, item.name)
    })
  },
  'find returns data in correct attributes': function(assert, beforeExit) {
    var assertMe = null
    var FindMeNow = s.define('FindMeNow', { title: Sequelize.STRING, content: Sequelize.TEXT })
    FindMeNow.drop(function() {
      FindMeNow.sync(function() {
        new FindMeNow({title: 'a title', content: 'a content'}).save(function(blubb) {
          assertMe = blubb
        })
      })
    })
    beforeExit(function() {
      assert.isNotNull(assertMe)
      assert.equal(assertMe.title, 'a title')
      assert.equal(assertMe.content, 'a content')
    })
  },
  'sqlResultToObject returns the correct object': function(assert) {
    var SqlResultToObjectTest = s.define('SqlResultToObject', {name: Sequelize.STRING})
    var toBeTested = SqlResultToObjectTest.sqlResultToObject({
      id: 1,
      name: 'foo'
    })
    assert.equal(toBeTested instanceof SqlResultToObjectTest, true)
    assert.equal(toBeTested.id, 1)
    assert.equal(toBeTested.name, 'foo')
  },
  'hasMany': function(assert) {
    var HasManyBlubb = s.define('HasManyBlubb', {})
    Day.hasMany('HasManyBlubbs', HasManyBlubb)
    assert.isDefined(new Day({name:''}).getHasManyBlubbs)
  },
  'hasMany: set association': function(assert, beforeExit) {
    var assoc = null
    var Character = s.define('Character', {})
    var Word = s.define('Word', {})
    Character.hasMany('Words', Word, 'Characters')

    Sequelize.chainQueries([
      {drop: Character}, {drop: Word}, {prepareAssociations: Word}, {prepareAssociations: Character}, {sync: Word}, {sync: Character}
    ],
    function() {
      var Association = s.tables.CharactersWords.klass
      Association.sync(function() {
        var w = new Word()
        var c1 = new Character()
        var c2 = new Character()
        Sequelize.chainQueries([{save: w}, {save: c1}, {save: c2}], function() {
          w.setCharacters([c1, c2], function(associations) {
            assoc = associations
          })
        })
      })
    })

    beforeExit(function() {
      assert.isNotNull(assoc)
      assert.equal(assoc.length, 2)
    })
  },
  'hasOne': function(assert) {
    var s = new Sequelize('sequelize_test', 'root', null, {disableLogging: true})
    var Day = s.define('Day2', { name: Sequelize.TEXT })
    var HasOneBlubb = s.define('HasOneBlubb', {})
    Day.hasOne('HasOneBlubb', HasOneBlubb)
    assert.isDefined(new Day({name:''}).getHasOneBlubb)
  },
  'hasOne set association': function(assert, beforeExit) {
    var s2 = new Sequelize('sequelize_test', 'root', null, {disableLogging: true})
    var Task = s2.define('Task', {title: Sequelize.STRING})
    var Deadline = s2.define('Deadline', {date: Sequelize.DATE})

    var taskAssoc = Task.hasOne('deadline', Deadline)
    Deadline.belongsTo('task', Task, taskAssoc)

    var task = new Task({ title:'do smth' })
    var deadline = new Deadline({date: new Date()})
    var assertMe = null

    Sequelize.chainQueries([{sync: s2}, {drop: s2}, {sync: s2}, {save: task}, {save: deadline}], function() {
      task.setDeadline(deadline, function(_deadline) {
        assertMe = _deadline
      })
    })

    beforeExit(function() {
      assert.isNotNull(assertMe)
      assert.eql(assertMe, deadline)
    })
  },
  'belongsTo': function(assert) {
    var BelongsToBlubb = s.define('BelongsToBlubb', {})
    var Day = s.define('Day2', { name: Sequelize.TEXT })
    var assoc = Day.hasOne('asd', BelongsToBlubb)
    Day.belongsTo('BelongsToBlubb', BelongsToBlubb, assoc)
    assert.isDefined(new Day({name:''}).getBelongsToBlubb)
  },
  'belongsTo: set association': function(assert, beforeExit) {
    var s2 = new Sequelize('sequelize_test', 'root', null, {disableLogging: true})
    var Task = s2.define('Task', {title: Sequelize.STRING})
    var Deadline = s2.define('Deadline', {date: Sequelize.DATE})
    var allowExit = false

    var assoc = Task.hasOne('deadline', Deadline)
    Deadline.belongsTo('task', Task, assoc)

    var task = new Task({ title:'do smth' })
    var deadline = new Deadline({date: new Date()})

    Sequelize.chainQueries([{drop: s2}, {sync: s2}], function() {
      task.save(function() {
        deadline.save(function() {
          assert.isDefined(deadline.id)
          assert.isNotNull(deadline.id)
          deadline.setTask(task, function(_task) {
            assert.isNotNull(_task)
            assert.eql(_task.id, task.id)
            allowExit = true
          })
        })
      })
    })

    beforeExit(function() {
      assert.equal(allowExit, true)
    })
  },
  'identifier': function(assert) {
    assert.equal(s.define('Identifier', {}).identifier, 'identifierId')
  },
  'values': function(assert) {
    var day = new Day({name: 's'})
    assert.eql(day.values, { name: "s", createdAt: null, updatedAt: null})
  },
  'save should set the id of a newly created object': function(assert, beforeExit) {
    var subject = null
    var SaveTest = s.define('SaveTest', {name: Sequelize.STRING})
    SaveTest.drop(function(){
      SaveTest.sync(function() {
         new SaveTest({name:'s'}).save(function(_saveTest){
          subject = _saveTest
        })
      })
    })

    beforeExit(function() {
      assert.isNotNull(subject.id)
    })
  },
  'updateAttributes should update available attributes': function(assert, beforeExit) {
    var subject = null
    var UpdateMe = s.define('UpdateMe', {name: Sequelize.STRING})
    Sequelize.chainQueries([{drop: UpdateMe}, {sync: UpdateMe}], function() {
      new UpdateMe({name:'Monday'}).save(function(u) {
        u.updateAttributes({name: 'Sunday', foo: 'bar'}, function(u) {
          subject = u
        })
      })
    })

    beforeExit(function() {
      assert.isNotNull(subject)
      assert.equal(subject.name, 'Sunday')
      assert.isUndefined(subject.foo)
    })
  },
  'destroy should make the object unavailable': function(assert, beforeExit) {
    var subject = 1
    var UpdateAttributesTest = s.define('UpdateAttributeTest', {name: Sequelize.STRING})
    Sequelize.chainQueries([{drop: UpdateAttributesTest}, {sync: UpdateAttributesTest}], function() {
      new UpdateAttributesTest({name:'Monday'}).save(function(day) {
        day.destroy(function() {
          UpdateAttributesTest.find(day.id, function(result) {
            subject = result
          })
        })
      })
    })
    beforeExit(function() {
      assert.isNull(subject)
    })
  },
  'boolean ==> save': function(assert, beforeExit) {
    var BooleanTest = s.define("BooleanTest", {flag: Sequelize.BOOLEAN})
    var testIsFinished = false
    BooleanTest.sync(function() {
      new BooleanTest({flag: true}).save(function(obj) {
        assert.equal(obj.flag, true)
        obj.updateAttributes({flag: false}, function(obj2) {
          assert.equal(obj2.flag, false)
          testIsFinished = true
        })
      })
    })
    beforeExit(function() { assert.equal(true, testIsFinished) })
  },
  'sync ==> failure': function(assert, beforeExit) {
    var testIsFinished = false,
        sequelizeWithInvalidCredentials = new Sequelize('foo', 'bar', 'barfoos'),
        Fail = sequelizeWithInvalidCredentials.define('Fail', {})

    Fail.sync(function(table, err) {
      assert.isDefined(err)
      assert.isDefined(err.message)
      testIsFinished = true
    })
    beforeExit(function() { assert.equal(testIsFinished, true) })
  },
  'drop ==> failure': function(assert, beforeExit) {
    var testIsFinished = false,
        sequelizeWithInvalidCredentials = new Sequelize('foo', 'bar', 'barfoos'),
        Fail = sequelizeWithInvalidCredentials.define('Fail', {})

    Fail.drop(function(table, err) {
      assert.isDefined(err)
      assert.isDefined(err.message)
      testIsFinished = true
    })
    beforeExit(function() { assert.equal(testIsFinished, true) })
  },
  'default values': function(assert) {
    var DefaultTest = s.define("DefaultTest", {
      aString: { type: Sequelize.STRING, allowNull: false, default: 'woot'},
      aNumber: { type: Sequelize.INTEGER, allowNull: true},
      aBoolean: { type: Sequelize.BOOLEAN, allowNull: false, default: false},
      aText: { type: Sequelize.TEXT, allowNull: true }
    }),
    instance = new DefaultTest({})

    assert.eql(instance.aString, 'woot')
    assert.isUndefined(instance.aNumber)
    assert.eql(instance.aBoolean, false)
    assert.isUndefined(instance.aText)
  }
}<|endoftext|>


<commit_before>var apiBenchmark = require('./../../index');
var should = require('should');
var TestServers = require('http-test-servers');

describe('measure function', function(){

  var testServers,
      server = { "My api": { port: 3006, delay: 0 },
                 "My slow api": { port: 3007, delay: 100 }},
      serversToBenchmark = { "My api": "http://localhost:3006"},
      slowServersToBenchmark = { "My api": "http://localhost:3007"},
      endpoints = {
        simpleRoute: '/getJson',
        secondaryRoute: '/getJson2',
        postRoute: {
          route: '/postJson',
          method: 'post',
          data: {
            test: true,
            someData: 'someStrings'
          }
        },
        deleteRoute: {
          route: '/deleteMe?test=true',
          method: 'delete'
        },
        errorRoute: {
          route: '/errorRoute',
          method: 'get',
          statusCode: 403
        }
      };

  before(function(done){

    var serversToStart = new TestServers(endpoints, server);
    serversToStart.start(function(httpTestServers){
      testServers = httpTestServers;
      done();
    });
  });

  after(function(done){
    testServers.kill(done);
  });

  it('should correctly measure the performances of the service', function(done) {
    apiBenchmark.measure(serversToBenchmark, { simpleRoute: endpoints.simpleRoute, postRoute: endpoints.postRoute }, function(err, results){
      results['My api'].should.not.be.eql(null);
      done();
    });
  });

  it('should correctly perform the minimum number of samples if we set minSamples and maxTime in runMode=sequence', function(done) {
    apiBenchmark.measure(serversToBenchmark, { simpleRoute: endpoints.simpleRoute}, { minSamples: 15, maxTime: 10, runMode: 'sequence' }, function(err, results){
      results['My api'].simpleRoute.stats.sample.length.should.be.eql(15);
      done();
    });
  });

  it('should correctly perform parallel requests to the service with runMode=parallel', function(done) {

    var parallelRequests = 50;

    apiBenchmark.measure(serversToBenchmark, { simpleRoute: endpoints.simpleRoute }, { runMode: 'parallel', minSamples: parallelRequests }, function(err, results){
      results['My api'].should.not.be.eql(null);
      results['My api'].simpleRoute.stats.sample.length.should.be.eql(parallelRequests);
      done();
    });
  });

  it('should correctly display hrefs for each result', function(done) {
    apiBenchmark.measure(serversToBenchmark, { simpleRoute: endpoints.simpleRoute }, function(err, results){
      results['My api'].simpleRoute.href.should.be.eql("http://localhost:3006/getJson");
      done();
    });
  });

  it('should correctly raise an exception if the optional expectedStatusCode is specified and incorrect', function(done) {

    var routesToBenchmark = {
      simpleRoute: {
        route: "/getJson",
        expectedStatusCode: 200
      },
      errorRoute: {
        route: "/errorRoute",
        expectedStatusCode: 200
      }
    };

    apiBenchmark.measure(serversToBenchmark, routesToBenchmark, function(err, results){
      err.should.be.eql("Expected Status code was 200 but I got a 403 for My api/errorRoute");
      should.not.exist(results);
      done();
    });
  });

  it('should correctly raise an exception if the optional maxMean is specified and out of range', function(done) {

    var routesToBenchmark = {
      simpleRoute: {
        route: "/getJson",
        maxMean: 0.080
      }
    };

    apiBenchmark.measure(slowServersToBenchmark, routesToBenchmark, function(err, results){
      err.should.be.eql("Mean should be below 0.08 for My api/simpleRoute");
      should.not.exist(results);
      done();
    });
  });

  it('should just collect the results including the errors in case of error and option stopOnError=false', function(done) {

    var routesToBenchmark = {
      simpleRoute: {
        route: "/getJson",
        expectedStatusCode: 200
      },
      errorRoute: {
        route: "/errorRoute",
        expectedStatusCode: 200
      }
    };

    apiBenchmark.measure(serversToBenchmark, routesToBenchmark, { stopOnError: false }, function(err, results){
      results['My api'].should.not.be.eql(null);
      results['My api'].errorRoute.errors['httpStatusCodeNotMatching'].length.should.be.above(0);
      err['My api'].errorRoute.should.be.eql(results['My api'].errorRoute.errors);
      done();
    });

  });

  it('should work without the optional options parameter', function(done) {
    apiBenchmark.measure(serversToBenchmark, { simpleRoute: endpoints.simpleRoute }, function(err, results){
      results['My api'].should.not.be.eql(null);
      done();
    });
  });

  it('should correctly handle post routes', function(done) {
    apiBenchmark.measure(serversToBenchmark, { postRoute: endpoints.postRoute }, function(err, results){
      results['My api'].postRoute.should.not.be.eql(null);
      done();
    });
  });

  it('should correctly handle delete routes', function(done) {
    apiBenchmark.measure(serversToBenchmark, { deleteRoute: endpoints.deleteRoute }, function(err, results){
      results['My api'].deleteRoute.should.not.be.eql(null);
      done();
    });
  });
});<commit_msg>Reduced samples for the maxMean test<commit_after>var apiBenchmark = require('./../../index');
var should = require('should');
var TestServers = require('http-test-servers');

describe('measure function', function(){

  var testServers,
      server = { "My api": { port: 3006, delay: 0 },
                 "My slow api": { port: 3007, delay: 100 }},
      serversToBenchmark = { "My api": "http://localhost:3006"},
      slowServersToBenchmark = { "My api": "http://localhost:3007"},
      endpoints = {
        simpleRoute: '/getJson',
        secondaryRoute: '/getJson2',
        postRoute: {
          route: '/postJson',
          method: 'post',
          data: {
            test: true,
            someData: 'someStrings'
          }
        },
        deleteRoute: {
          route: '/deleteMe?test=true',
          method: 'delete'
        },
        errorRoute: {
          route: '/errorRoute',
          method: 'get',
          statusCode: 403
        }
      };

  before(function(done){

    var serversToStart = new TestServers(endpoints, server);
    serversToStart.start(function(httpTestServers){
      testServers = httpTestServers;
      done();
    });
  });

  after(function(done){
    testServers.kill(done);
  });

  it('should correctly measure the performances of the service', function(done) {
    apiBenchmark.measure(serversToBenchmark, { simpleRoute: endpoints.simpleRoute, postRoute: endpoints.postRoute }, function(err, results){
      results['My api'].should.not.be.eql(null);
      done();
    });
  });

  it('should correctly perform the minimum number of samples if we set minSamples and maxTime in runMode=sequence', function(done) {
    apiBenchmark.measure(serversToBenchmark, { simpleRoute: endpoints.simpleRoute}, { minSamples: 15, maxTime: 10, runMode: 'sequence' }, function(err, results){
      results['My api'].simpleRoute.stats.sample.length.should.be.eql(15);
      done();
    });
  });

  it('should correctly perform parallel requests to the service with runMode=parallel', function(done) {

    var parallelRequests = 50;

    apiBenchmark.measure(serversToBenchmark, { simpleRoute: endpoints.simpleRoute }, { runMode: 'parallel', minSamples: parallelRequests }, function(err, results){
      results['My api'].should.not.be.eql(null);
      results['My api'].simpleRoute.stats.sample.length.should.be.eql(parallelRequests);
      done();
    });
  });

  it('should correctly display hrefs for each result', function(done) {
    apiBenchmark.measure(serversToBenchmark, { simpleRoute: endpoints.simpleRoute }, function(err, results){
      results['My api'].simpleRoute.href.should.be.eql("http://localhost:3006/getJson");
      done();
    });
  });

  it('should correctly raise an exception if the optional expectedStatusCode is specified and incorrect', function(done) {

    var routesToBenchmark = {
      simpleRoute: {
        route: "/getJson",
        expectedStatusCode: 200
      },
      errorRoute: {
        route: "/errorRoute",
        expectedStatusCode: 200
      }
    };

    apiBenchmark.measure(serversToBenchmark, routesToBenchmark, function(err, results){
      err.should.be.eql("Expected Status code was 200 but I got a 403 for My api/errorRoute");
      should.not.exist(results);
      done();
    });
  });

  it('should correctly raise an exception if the optional maxMean is specified and out of range', function(done) {

    var routesToBenchmark = {
      simpleRoute: {
        route: "/getJson",
        maxMean: 0.080
      }
    };

    apiBenchmark.measure(slowServersToBenchmark, routesToBenchmark, { minSamples: 2 }, function(err, results){
      err.should.be.eql("Mean should be below 0.08 for My api/simpleRoute");
      should.not.exist(results);
      done();
    });
  });

  it('should just collect the results including the errors in case of error and option stopOnError=false', function(done) {

    var routesToBenchmark = {
      simpleRoute: {
        route: "/getJson",
        expectedStatusCode: 200
      },
      errorRoute: {
        route: "/errorRoute",
        expectedStatusCode: 200
      }
    };

    apiBenchmark.measure(serversToBenchmark, routesToBenchmark, { stopOnError: false }, function(err, results){
      results['My api'].should.not.be.eql(null);
      results['My api'].errorRoute.errors['httpStatusCodeNotMatching'].length.should.be.above(0);
      err['My api'].errorRoute.should.be.eql(results['My api'].errorRoute.errors);
      done();
    });

  });

  it('should work without the optional options parameter', function(done) {
    apiBenchmark.measure(serversToBenchmark, { simpleRoute: endpoints.simpleRoute }, function(err, results){
      results['My api'].should.not.be.eql(null);
      done();
    });
  });

  it('should correctly handle post routes', function(done) {
    apiBenchmark.measure(serversToBenchmark, { postRoute: endpoints.postRoute }, function(err, results){
      results['My api'].postRoute.should.not.be.eql(null);
      done();
    });
  });

  it('should correctly handle delete routes', function(done) {
    apiBenchmark.measure(serversToBenchmark, { deleteRoute: endpoints.deleteRoute }, function(err, results){
      results['My api'].deleteRoute.should.not.be.eql(null);
      done();
    });
  });
});<|endoftext|>


<commit_before>'use strict';

var assert = require('assert');
var formsAngular = require('../../server/data_form.js');
var express = require('express');
var async = require('async');
var path = require('path');
var fs = require('fs');
var _ = require('underscore');
var exec = require('child_process').exec;
var mongoose = require('mongoose');

describe('API', function () {

  var fng, app;

  before(function (done) {
    app = express();

    fng = new (formsAngular)(app, {urlPrefix: '/api/'});

    mongoose.connect('localhost', 'forms-ng_test');
    mongoose.connection.on('error', function () {
      console.error('connection error', arguments);
    });

    mongoose.connection.on('open', function () {
      // Bootstrap models
      var modelsPath = path.join(__dirname, '/models');
      fs.readdirSync(modelsPath).forEach(function (file) {
        var fname = modelsPath + '/' + file;
        if (fs.statSync(fname).isFile()) {
          fng.addResource(file.slice(0, -3), require(fname));
        }
      });
    });

    // Import test data
    var dataPath = path.join(__dirname, 'data');
    async.each(fs.readdirSync(dataPath), function (file, callback) {
      var fname = dataPath + '/' + file;
      if (fs.statSync(fname).isFile()) {
        exec('mongoimport --db forms-ng_test --drop --collection ' + file.slice(0, 1) + 's --jsonArray < ' + fname, callback);
      }
    }, function (err) {
      if (err) {
        console.log('Problem importing test data ' + err.message);
      } else {
        done();
      }
    });
  });

  after(function (done) {
    mongoose.connection.db.dropDatabase(function () {
      mongoose.disconnect(function () {
        done();
      });
    });
  });

  it('returns models', function () {
    var mockReq = null;
    var mockRes = {
      send: function (models) {
        assert.equal(models.length, 11);
        assert(_.find(models, function (resource) {
          return resource.resourceName === 'b_using_options';
        }).options.hide.indexOf('login') > -1, 'must send login as a hidden field');
      }
    };
    fng.models()(mockReq, mockRes);
  });

  it('returns straight schema', function (done) {
    var mockReq = {params : {resourceName: 'a_unadorned_mongoose'}};
    var mockRes = {
      send: function (schema) {
        schema = JSON.parse(schema);
        var keys = Object.keys(schema);
        assert.equal(keys.length, 8);
        assert.equal(schema[keys[0]].path, 'surname');
        assert.equal(schema[keys[1]].path, 'forename');
        assert.equal(schema[keys[2]].path, 'phone');
        assert.equal(schema[keys[3]].path, 'weight');
        assert.equal(schema[keys[4]].path, 'eyeColour');
        assert.equal(schema[keys[5]].path, 'dateOfBirth');
        assert.equal(schema[keys[6]].path, 'accepted');
        assert.equal(schema[keys[7]].path, '_id');
        done();
      }
    };
    fng.schema()(mockReq, mockRes);
  });

  it('returns nested schema', function (done) {
    var mockReq = {params : {resourceName: 'f_nested_schema'}};
    var mockRes = {
      send: function (schema) {
        schema = JSON.parse(schema);
        var keys = Object.keys(schema);
        assert.equal(keys.length, 5);
        assert.equal(keys[0], 'surname');
        assert.equal(schema[keys[0]].path, 'surname');
        assert.equal(keys[1], 'forename');
        assert.equal(schema[keys[1]].path, 'forename');
        assert.equal(keys[2], 'aTest');
        assert.equal(schema[keys[2]].path, 'aTest');
        assert.equal(keys[3], 'exams');
        keys = Object.keys(schema[keys[3]].schema);
        assert.equal(keys.length, 6);
        assert.equal(keys[0], 'subject');
        assert.equal(schema.exams.schema[keys[0]].path, 'subject');
        assert.equal(keys[3], 'result');
        assert.equal(schema.exams.schema[keys[3]].path, 'result');
        done();
      }
    };
    fng.schema()(mockReq, mockRes);
  });

  it('returns forms schema', function (done) {
    var mockReq = {params : {resourceName: 'b_using_options', formName: 'justnameandpostcode'}};
    var mockRes = {
      send: function (schema) {
        schema = JSON.parse(schema);
        var keys = Object.keys(schema);
        assert.equal(keys.length, 4);
        assert.equal(schema[keys[0]].path, 'surname');
        assert.equal(schema[keys[1]].path, 'address.postcode');
        done();
      }
    };
    fng.schema()(mockReq, mockRes);
  });

  it.skip('allows form schemas to override nested schemas', function (done) {
    var mockReq = {params : {resourceName: 'f_nested_schema', formName: 'ResultsOnly'}};
    var mockRes = {
      send: function (schema) {
        schema = JSON.parse(schema);
        var keys = Object.keys(schema);
        assert.equal(keys.length, 3);
        assert.equal(schema[keys[0]].path, 'surname');
        assert.equal(keys[0], 'surname');
        assert.equal(schema[keys[1]].path, 'forename');
        assert.equal(keys[1], 'forename');
        assert.equal(keys[2], 'exams');
        keys = Object.keys(schema[keys[2]].schema);
        assert.equal(keys.length, 2);
        assert.equal(keys[0], 'subject');
        assert.equal(schema.exams.schema[keys[0]].path, 'subject');
        assert.equal(keys[1], 'result');
        assert.equal(schema.exams.schema[keys[1]].path, 'result');
        done();
      }
    };
    fng.schema()(mockReq, mockRes);
  });

});<commit_msg>Add test for using complete schema within a sub schema in a form schema<commit_after>'use strict';

var assert = require('assert');
var formsAngular = require('../../server/data_form.js');
var express = require('express');
var async = require('async');
var path = require('path');
var fs = require('fs');
var _ = require('underscore');
var exec = require('child_process').exec;
var mongoose = require('mongoose');

describe('API', function () {

  var fng, app;

  before(function (done) {
    app = express();

    fng = new (formsAngular)(app, {urlPrefix: '/api/'});

    mongoose.connect('localhost', 'forms-ng_test');
    mongoose.connection.on('error', function () {
      console.error('connection error', arguments);
    });

    mongoose.connection.on('open', function () {
      // Bootstrap models
      var modelsPath = path.join(__dirname, '/models');
      fs.readdirSync(modelsPath).forEach(function (file) {
        var fname = modelsPath + '/' + file;
        if (fs.statSync(fname).isFile()) {
          fng.addResource(file.slice(0, -3), require(fname));
        }
      });
    });

    // Import test data
    var dataPath = path.join(__dirname, 'data');
    async.each(fs.readdirSync(dataPath), function (file, callback) {
      var fname = dataPath + '/' + file;
      if (fs.statSync(fname).isFile()) {
        exec('mongoimport --db forms-ng_test --drop --collection ' + file.slice(0, 1) + 's --jsonArray < ' + fname, callback);
      }
    }, function (err) {
      if (err) {
        console.log('Problem importing test data ' + err.message);
      } else {
        done();
      }
    });
  });

  after(function (done) {
    mongoose.connection.db.dropDatabase(function () {
      mongoose.disconnect(function () {
        done();
      });
    });
  });

  it('returns models', function () {
    var mockReq = null;
    var mockRes = {
      send: function (models) {
        assert.equal(models.length, 11);
        assert(_.find(models, function (resource) {
          return resource.resourceName === 'b_using_options';
        }).options.hide.indexOf('login') > -1, 'must send login as a hidden field');
      }
    };
    fng.models()(mockReq, mockRes);
  });

  it('returns straight schema', function (done) {
    var mockReq = {params : {resourceName: 'a_unadorned_mongoose'}};
    var mockRes = {
      send: function (schema) {
        schema = JSON.parse(schema);
        var keys = Object.keys(schema);
        assert.equal(keys.length, 8);
        assert.equal(schema[keys[0]].path, 'surname');
        assert.equal(schema[keys[1]].path, 'forename');
        assert.equal(schema[keys[2]].path, 'phone');
        assert.equal(schema[keys[3]].path, 'weight');
        assert.equal(schema[keys[4]].path, 'eyeColour');
        assert.equal(schema[keys[5]].path, 'dateOfBirth');
        assert.equal(schema[keys[6]].path, 'accepted');
        assert.equal(schema[keys[7]].path, '_id');
        done();
      }
    };
    fng.schema()(mockReq, mockRes);
  });

  it('returns nested schema', function (done) {
    var mockReq = {params : {resourceName: 'f_nested_schema'}};
    var mockRes = {
      send: function (schema) {
        schema = JSON.parse(schema);
        var keys = Object.keys(schema);
        assert.equal(keys.length, 5);
        assert.equal(keys[0], 'surname');
        assert.equal(schema[keys[0]].path, 'surname');
        assert.equal(keys[1], 'forename');
        assert.equal(schema[keys[1]].path, 'forename');
        assert.equal(keys[2], 'aTest');
        assert.equal(schema[keys[2]].path, 'aTest');
        assert.equal(keys[3], 'exams');
        keys = Object.keys(schema[keys[3]].schema);
        assert.equal(keys.length, 6);
        assert.equal(keys[0], 'subject');
        assert.equal(schema.exams.schema[keys[0]].path, 'subject');
        assert.equal(keys[3], 'result');
        assert.equal(schema.exams.schema[keys[3]].path, 'result');
        done();
      }
    };
    fng.schema()(mockReq, mockRes);
  });

  it('returns forms schema', function (done) {
    var mockReq = {params : {resourceName: 'b_using_options', formName: 'justnameandpostcode'}};
    var mockRes = {
      send: function (schema) {
        schema = JSON.parse(schema);
        var keys = Object.keys(schema);
        assert.equal(keys.length, 4);
        assert.equal(schema[keys[0]].path, 'surname');
        assert.equal(schema[keys[1]].path, 'address.postcode');
        done();
      }
    };
    fng.schema()(mockReq, mockRes);
  });

  it('supports nested schemas within form schemas', function (done) {
    var mockReq = {params : {resourceName: 'f_nested_schema', formName: 'EnglishAndMaths'}};
    var mockRes = {
      send: function (schema) {
        schema = JSON.parse(schema);
        var keys = Object.keys(schema);
        assert.equal(keys.length, 3);
        assert.equal(schema[keys[0]].path, 'surname');
        assert.equal(keys[0], 'surname');
        assert.equal(schema[keys[1]].path, 'forename');
        assert.equal(keys[1], 'forename');
        assert.equal(keys[2], 'exams');
        keys = Object.keys(schema[keys[2]].schema);
        assert.equal(keys.length, 6);
        assert.equal(keys[0], 'subject');
        assert.equal(schema.exams.schema[keys[0]].path, 'subject');
        assert.equal(keys[3], 'result');
        assert.equal(schema.exams.schema[keys[3]].path, 'result');
        done();
      }
    };
    fng.schema()(mockReq, mockRes);
  });

  it('allows form schemas to override nested schemas', function (done) {
    var mockReq = {params : {resourceName: 'f_nested_schema', formName: 'ResultsOnly'}};
    var mockRes = {
      send: function (schema) {
        schema = JSON.parse(schema);
        var keys = Object.keys(schema);
        assert.equal(keys.length, 3);
        assert.equal(schema[keys[0]].path, 'surname');
        assert.equal(keys[0], 'surname');
        assert.equal(schema[keys[1]].path, 'forename');
        assert.equal(keys[1], 'forename');
        assert.equal(keys[2], 'exams');
        keys = Object.keys(schema[keys[2]].schema);
        assert.equal(keys.length, 2);
        assert.equal(keys[0], 'subject');
        assert.equal(schema.exams.schema[keys[0]].path, 'subject');
        assert.equal(keys[1], 'result');
        assert.equal(schema.exams.schema[keys[1]].path, 'result');
        done();
      }
    };
    fng.schema()(mockReq, mockRes);
  });

});<|endoftext|>


<commit_before>var { promisify } = require('util');
var assert = require('assert');

var { ADMIN_USER, TEST_USER } = require('../fixtures.js');
const apiClient = require('../api-client.js');

const get = promisify(apiClient.get);
const logout = promisify(apiClient.logout);
const loginAs = promisify(apiClient.loginAs);
const signupAs = promisify(apiClient.signupAs);
const getUser = promisify(apiClient.getUser);

describe('auth api', () => {
  describe('login with email', () => {
    it('succeeds', async () => {
      const { response, body } = await loginAs(ADMIN_USER);
      const cookies = ((response.headers || {})['set-cookie'] || []).join(' ');
      assert(/whydSid\=/.test(cookies));
      assert(body.redirect);
    });

    it('access to personal /stream requires login', async () => {
      const { body } = await get({}, '/stream?format=json');
      assert.equal(body.error, 'Please login first');
    });

    it('gives access to personal /stream', async () => {
      const { jar } = await loginAs(ADMIN_USER);
      const { response, body } = await get(jar, '/stream?format=json');
      assert.equal(response.statusCode, 200);
      assert.ifError(body.error);
      assert(body.join); // check that it's an array
    });

    it('fails if wrong email', async () => {
      const { body } = await loginAs({ ...ADMIN_USER, email: 'qq' });
      assert(/email/.test(body.error));
    });

    it('fails if wrong password', async () => {
      const { body } = await loginAs({ ...ADMIN_USER, md5: 'qq' });
      assert(body.wrongPassword);
    });
  });

  describe('logout', () => {
    it('denies access to personal /stream', async () => {
      const { jar: loginJar } = await loginAs(ADMIN_USER);
      const { jar } = await logout(loginJar);
      const { body } = await get(jar, '/stream?format=json');
      assert(/login/.test(body.error));
    });
  });

  //describe('forgot password', () => {); // TODO <= mock emails

  // Register / sign up a new user

  describe('signup with md5', () => {
    it('gives access to personal /stream', async () => {
      const { jar, body: signupBody } = await signupAs(TEST_USER);
      assert.ifError(signupBody.error);
      const { response, body } = await get(jar, '/stream?format=json');
      assert.equal(response.statusCode, 200);
      assert.ifError(body.error);
      assert(body.join); // check that it's an array
    });

    it('fails if password is missing', async () => {
      const userWithMissingPwd = {
        ...TEST_USER,
        pwd: '',
        password: '',
        md5: ''
      };
      const { jar, body: signupBody } = await signupAs(userWithMissingPwd);
      assert.equal(signupBody.error, 'Please enter a password');
      const { body } = await get(jar, '/stream?format=json');
      assert.equal(body.error, 'Please login first');
    });
  });

  describe('signup with secure hash', () => {
    const genSecureUser = (() => {
      let globalNumber = 0;
      return (number = ++globalNumber) => ({
        name: `secure user ${number}`,
        email: `secure-user-${number}@openwhyd.org`,
        password: `mySecurePassword${number}`
      });
    })();

    it('can create account', async () => {
      const { jar, body } = await signupAs(genSecureUser());
      assert.ifError(body.error);
    });

    it('gives access to personal /stream', async () => {
      const { jar } = await signupAs(genSecureUser());
      const { body } = await get(jar, '/stream?format=json');
      assert.ifError(body.error);
    });

    it.skip('can login', async () => {
      const { jar, body } = await loginAs(secureUser); // no md5 is provided
      assert.ifError(body.error);
      assert(jar);
    });

    it.skip('holds secure hash', async () => {
      const { jar } = await loginAs(secureUser);
      const { body } = await getUser(jar, {});
      assert.ifError(body.error);
      assert.assert(body.sha1);
    });
  });
});
<commit_msg>refacto: move genSecureUser() to top-level, for re-use<commit_after>var { promisify } = require('util');
var assert = require('assert');

var { ADMIN_USER, TEST_USER } = require('../fixtures.js');
const apiClient = require('../api-client.js');

const get = promisify(apiClient.get);
const logout = promisify(apiClient.logout);
const loginAs = promisify(apiClient.loginAs);
const signupAs = promisify(apiClient.signupAs);
const getUser = promisify(apiClient.getUser);

const genSecureUser = (() => {
  let globalNumber = 0;
  return (number = ++globalNumber) => ({
    name: `secure user ${number}`,
    email: `secure-user-${number}@openwhyd.org`,
    password: `mySecurePassword${number}`
  });
})();

describe('auth api', () => {
  describe('login with email', () => {
    it('succeeds', async () => {
      const { response, body } = await loginAs(ADMIN_USER);
      const cookies = ((response.headers || {})['set-cookie'] || []).join(' ');
      assert(/whydSid\=/.test(cookies));
      assert(body.redirect);
    });

    it('access to personal /stream requires login', async () => {
      const { body } = await get({}, '/stream?format=json');
      assert.equal(body.error, 'Please login first');
    });

    it('gives access to personal /stream', async () => {
      const { jar } = await loginAs(ADMIN_USER);
      const { response, body } = await get(jar, '/stream?format=json');
      assert.equal(response.statusCode, 200);
      assert.ifError(body.error);
      assert(body.join); // check that it's an array
    });

    it('fails if wrong email', async () => {
      const { body } = await loginAs({ ...ADMIN_USER, email: 'qq' });
      assert(/email/.test(body.error));
    });

    it('fails if wrong password', async () => {
      const { body } = await loginAs({ ...ADMIN_USER, md5: 'qq' });
      assert(body.wrongPassword);
    });
  });

  describe('logout', () => {
    it('denies access to personal /stream', async () => {
      const { jar: loginJar } = await loginAs(ADMIN_USER);
      const { jar } = await logout(loginJar);
      const { body } = await get(jar, '/stream?format=json');
      assert(/login/.test(body.error));
    });
  });

  //describe('forgot password', () => {); // TODO <= mock emails

  // Register / sign up a new user

  describe('signup with md5', () => {
    it('gives access to personal /stream', async () => {
      const { jar, body: signupBody } = await signupAs(TEST_USER);
      assert.ifError(signupBody.error);
      const { response, body } = await get(jar, '/stream?format=json');
      assert.equal(response.statusCode, 200);
      assert.ifError(body.error);
      assert(body.join); // check that it's an array
    });

    it('fails if password is missing', async () => {
      const userWithMissingPwd = {
        ...TEST_USER,
        pwd: '',
        password: '',
        md5: ''
      };
      const { jar, body: signupBody } = await signupAs(userWithMissingPwd);
      assert.equal(signupBody.error, 'Please enter a password');
      const { body } = await get(jar, '/stream?format=json');
      assert.equal(body.error, 'Please login first');
    });
  });

  describe('signup with secure hash', () => {
    it('can create account', async () => {
      const { jar, body } = await signupAs(genSecureUser());
      assert.ifError(body.error);
    });

    it('gives access to personal /stream', async () => {
      const { jar } = await signupAs(genSecureUser());
      const { body } = await get(jar, '/stream?format=json');
      assert.ifError(body.error);
    });

    it.skip('can login', async () => {
      const { jar, body } = await loginAs(secureUser); // no md5 is provided
      assert.ifError(body.error);
      assert(jar);
    });

    it.skip('holds secure hash', async () => {
      const { jar } = await loginAs(secureUser);
      const { body } = await getUser(jar, {});
      assert.ifError(body.error);
      assert.assert(body.sha1);
    });
  });
});
<|endoftext|>