<commit_before>'use strict';

var _ = require('lodash');
var chai = require('chai');
var expect = chai.expect;

var Database = require('../../lib/database');
var FakeAdapter = require('../fakes/adapter');

require('../helpers/model');

var db,
  adapter,
  Article,
  Comment,
  User,
  article;

describe('Model.belongsTo', function() {
  beforeEach(function() {
    adapter = FakeAdapter.create({});
    db = Database.create({ adapter: adapter });

    var belongsTo = db.belongsTo;
    var attr = db.attr;

    Article = db.model('article').reopen({
      title: attr(),
      author: belongsTo('user'),
      authorKey: attr('author_id') // writable access to foreign key attr
    });
    Comment = db.model('comment').reopen({
      body: db.attr(),
      article: db.belongsTo('article')
    });
    User = db.model('user').reopen({
      username: attr()
    });
  });

  beforeEach(function() {
    article = Article.fresh({ id: 932, title: 'Azul News', authorKey: 623 });
  });

  beforeEach(function() {
    adapter.intercept(/select.*from "users"/i, {
      fields: ['id', 'username'],
      rows: [{ id: 623, username: 'wbyoung' }]
    });
    adapter.intercept(/select.*from "articles"/i, {
      fields: ['id', 'title', 'author_id'],
      rows: [{ id: 448, title: 'Journal', 'author_id': 623 }]
    });
    adapter.intercept(/select.*from "comments"/i, {
      fields: ['id', 'body', 'article_id'],
      rows: [{ id: 384, body: 'Great Post!', 'article_id': 448 }]
    });
    adapter.intercept(/insert into "users"/i, {
      fields: ['id'],
      rows: [{ id: 838 }]
    });
  });

  describe('definition', function() {
    it('does not need to provide name', function() {
      Article.reopen({
        user: db.Model.belongsTo()
      });
      expect(article.userRelation._relatedModel).to.eql(db.model('user'));
    });

    it('uses the foreign key as the join key', function() {
      expect(article.authorRelation.joinKey).to.eql(article.authorRelation.foreignKey);
    });

    it('uses the primary key as the inverse key', function() {
      expect(article.authorRelation.inverseKey).to.eql(article.authorRelation.primaryKey);
    });
  });

  it('has related methods', function() {
    expect(Article.__class__.prototype).to.have.ownProperty('author');
    expect(Article.__class__.prototype).to.have.ownProperty('authorId');
    expect(article).to.respondTo('fetchAuthor');
    expect(article).to.respondTo('setAuthor');
  });

  describe('relation', function() {

    it('fetches related object', function(done) {
      article.fetchAuthor().then(function(user) {
        expect(user.attrs).to.eql({ id: 623, username: 'wbyoung' });
        expect(adapter.executedSQL()).to.eql([
          ['SELECT * FROM "users" WHERE "id" = ? LIMIT 1', [623]]
        ]);
      })
      .done(done, done);
    });

    it('caches the fetched object (one query for two fetches)', function(done) {
      article.fetchAuthor()
      .then(function() { return article.fetchAuthor(); })
      .then(function(user) {
        expect(user.attrs).to.eql({ id: 623, username: 'wbyoung' });
        expect(adapter.executedSQL()).to.eql([
          ['SELECT * FROM "users" WHERE "id" = ? LIMIT 1', [623]]
        ]);
      })
      .done(done, done);
    });

    it('gives an error when it cannot fetch the related object', function(done) {
      adapter.intercept(/select.*from "users"/i, {
        fields: ['id', 'username'],
        rows: []
      });
      article.fetchAuthor()
      .throw(new Error('Expected fetch to fail.'))
      .catch(function(e) {
        expect(e.message).to.match(/found no.*User.*author_id.*623/i);
      })
      .done(done, done);
    });

    it('does not fetch when the foreign key is not defined', function(done) {
      var unauthoredArticle = Article.fresh({ id: 932, title: 'Azul News' });
      unauthoredArticle.fetchAuthor().then(function(user) {
        expect(user).to.not.exist;
        expect(unauthoredArticle.author).to.not.exist;
        expect(adapter.executedSQL()).to.eql([]);
      })
      .done(done, done);
    });

    it('throws when attempting to access un-loaded item', function() {
      expect(function() {
        article.author;
      }).to.throw(/author.*not yet.*loaded/i);
    });

    it('allows access loaded item', function(done) {
      article.fetchAuthor().then(function() {
        expect(article.author.attrs).to.eql({ id: 623, username: 'wbyoung' });
      })
      .done(done, done);
    });

    it('does not load the item during model load', function(done) {
      Article.objects.fetchOne().then(function(fetchedArticle) {
        expect(function() {
          fetchedArticle.author;
        }).to.throw(/author.*not yet.*loaded/i);
      })
      .done(done, done);
    });
  });

  describe('helpers', function() {
    it('provides a getter method for the foreign key', function() {
      expect(article.authorId).to.eql(623);
    });

    it('does not provide a setter method for the foreign key', function() {
      expect(function() {
        article.authorId = 25;
      }).to.throw(/cannot set.*authorId/i);
    });

    it('allows create', function() {
      var user = article.createAuthor({ username: 'jill' });
      expect(article.author).to.equal(user);
      expect(user).to.to.be.an.instanceOf(User.__class__);
    });

    it('allows store with existing object', function(done) {
      article.author = User.fresh({ id: 3, username: 'jack' });
      article.save().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['UPDATE "articles" SET "title" = ?, "author_id" = ? ' +
           'WHERE "id" = ?', ['Azul News', 3, 932]]
        ]);
      })
      .done(done, done);
    });

    it('allows store with unsaved object', function(done) {
      var user = User.create({ username: 'jack' });
      article.author = user;
      article.save().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['INSERT INTO "users" ("username") VALUES (?) ' +
           'RETURNING "id"', ['jack']],
          ['UPDATE "articles" SET "title" = ?, "author_id" = ? ' +
           'WHERE "id" = ?', ['Azul News', 838, 932]]
        ]);
      })
      .done(done, done);
    });
  });

  describe('joins', function() {
    it('generates simple join queries', function(done) {
      Article.objects.join('author').fetch().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['SELECT "articles".* FROM "articles" ' +
           'INNER JOIN "users" ON "articles"."author_id" = "users"."id"', []]
        ]);
      })
      .done(done, done);
    });

    it('can be made unique', function(done) {
      Article.objects.join('author').unique().fetch().then(function() {<commit_msg>Added a test for creating relation via constructor.<commit_after>'use strict';

var _ = require('lodash');
var chai = require('chai');
var expect = chai.expect;

var Database = require('../../lib/database');
var FakeAdapter = require('../fakes/adapter');

require('../helpers/model');

var db,
  adapter,
  Article,
  Comment,
  User,
  article;

describe('Model.belongsTo', function() {
  beforeEach(function() {
    adapter = FakeAdapter.create({});
    db = Database.create({ adapter: adapter });

    var belongsTo = db.belongsTo;
    var attr = db.attr;

    Article = db.model('article').reopen({
      title: attr(),
      author: belongsTo('user'),
      authorKey: attr('author_id') // writable access to foreign key attr
    });
    Comment = db.model('comment').reopen({
      body: db.attr(),
      article: db.belongsTo('article')
    });
    User = db.model('user').reopen({
      username: attr()
    });
  });

  beforeEach(function() {
    article = Article.fresh({ id: 932, title: 'Azul News', authorKey: 623 });
  });

  beforeEach(function() {
    adapter.intercept(/select.*from "users"/i, {
      fields: ['id', 'username'],
      rows: [{ id: 623, username: 'wbyoung' }]
    });
    adapter.intercept(/select.*from "articles"/i, {
      fields: ['id', 'title', 'author_id'],
      rows: [{ id: 448, title: 'Journal', 'author_id': 623 }]
    });
    adapter.intercept(/select.*from "comments"/i, {
      fields: ['id', 'body', 'article_id'],
      rows: [{ id: 384, body: 'Great Post!', 'article_id': 448 }]
    });
    adapter.intercept(/insert into "users"/i, {
      fields: ['id'],
      rows: [{ id: 838 }]
    });
    adapter.intercept(/insert into "articles"/i, {
      fields: ['id'],
      rows: [{ id: 78 }]
    });
  });

  describe('definition', function() {
    it('does not need to provide name', function() {
      Article.reopen({
        user: db.Model.belongsTo()
      });
      expect(article.userRelation._relatedModel).to.eql(db.model('user'));
    });

    it('uses the foreign key as the join key', function() {
      expect(article.authorRelation.joinKey).to.eql(article.authorRelation.foreignKey);
    });

    it('uses the primary key as the inverse key', function() {
      expect(article.authorRelation.inverseKey).to.eql(article.authorRelation.primaryKey);
    });
  });

  it('has related methods', function() {
    expect(Article.__class__.prototype).to.have.ownProperty('author');
    expect(Article.__class__.prototype).to.have.ownProperty('authorId');
    expect(article).to.respondTo('fetchAuthor');
    expect(article).to.respondTo('setAuthor');
  });

  describe('relation', function() {

    it('fetches related object', function(done) {
      article.fetchAuthor().then(function(user) {
        expect(user.attrs).to.eql({ id: 623, username: 'wbyoung' });
        expect(adapter.executedSQL()).to.eql([
          ['SELECT * FROM "users" WHERE "id" = ? LIMIT 1', [623]]
        ]);
      })
      .done(done, done);
    });

    it('caches the fetched object (one query for two fetches)', function(done) {
      article.fetchAuthor()
      .then(function() { return article.fetchAuthor(); })
      .then(function(user) {
        expect(user.attrs).to.eql({ id: 623, username: 'wbyoung' });
        expect(adapter.executedSQL()).to.eql([
          ['SELECT * FROM "users" WHERE "id" = ? LIMIT 1', [623]]
        ]);
      })
      .done(done, done);
    });

    it('gives an error when it cannot fetch the related object', function(done) {
      adapter.intercept(/select.*from "users"/i, {
        fields: ['id', 'username'],
        rows: []
      });
      article.fetchAuthor()
      .throw(new Error('Expected fetch to fail.'))
      .catch(function(e) {
        expect(e.message).to.match(/found no.*User.*author_id.*623/i);
      })
      .done(done, done);
    });

    it('does not fetch when the foreign key is not defined', function(done) {
      var unauthoredArticle = Article.fresh({ id: 932, title: 'Azul News' });
      unauthoredArticle.fetchAuthor().then(function(user) {
        expect(user).to.not.exist;
        expect(unauthoredArticle.author).to.not.exist;
        expect(adapter.executedSQL()).to.eql([]);
      })
      .done(done, done);
    });

    it('throws when attempting to access un-loaded item', function() {
      expect(function() {
        article.author;
      }).to.throw(/author.*not yet.*loaded/i);
    });

    it('allows access loaded item', function(done) {
      article.fetchAuthor().then(function() {
        expect(article.author.attrs).to.eql({ id: 623, username: 'wbyoung' });
      })
      .done(done, done);
    });

    it('does not load the item during model load', function(done) {
      Article.objects.fetchOne().then(function(fetchedArticle) {
        expect(function() {
          fetchedArticle.author;
        }).to.throw(/author.*not yet.*loaded/i);
      })
      .done(done, done);
    });
  });

  describe('helpers', function() {
    it('provides a getter method for the foreign key', function() {
      expect(article.authorId).to.eql(623);
    });

    it('does not provide a setter method for the foreign key', function() {
      expect(function() {
        article.authorId = 25;
      }).to.throw(/cannot set.*authorId/i);
    });

    it('allows create', function() {
      var user = article.createAuthor({ username: 'jill' });
      expect(article.author).to.equal(user);
      expect(user).to.to.be.an.instanceOf(User.__class__);
    });

    it('allows store with existing object', function(done) {
      article.author = User.fresh({ id: 3, username: 'jack' });
      article.save().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['UPDATE "articles" SET "title" = ?, "author_id" = ? ' +
           'WHERE "id" = ?', ['Azul News', 3, 932]]
        ]);
      })
      .done(done, done);
    });

    it('allows store with unsaved object', function(done) {
      var user = User.create({ username: 'jack' });
      article.author = user;
      article.save().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['INSERT INTO "users" ("username") VALUES (?) ' +
           'RETURNING "id"', ['jack']],
          ['UPDATE "articles" SET "title" = ?, "author_id" = ? ' +
           'WHERE "id" = ?', ['Azul News', 838, 932]]
        ]);
      })
      .done(done, done);
    });

    it('allows store via constructor', function(done) {
      var user = User.create({ username: 'jack' });
      article = Article.create({ title: 'Azul News', author: user });
      article.save().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['INSERT INTO "users" ("username") VALUES (?) ' +
           'RETURNING "id"', ['jack']],
          ['INSERT INTO "articles" ("title", "author_id") VALUES (?, ?) ' +
           'RETURNING "id"', ['Azul News', 838]]
        ]);
      })
      .done(done, done);
    });
  });

  describe('joins', function() {
    it('generates simple join queries', function(done) {
      Article.objects.join('author').fetch().then(function() {
        expect(adapter.executedSQL()).to.eql([
          ['SELECT "articles".* FROM "articles" ' +
           'INNER JOIN "users" ON "articles"."author_id" = "users"."id"', []]
        ]);
      })
      .done(done, done);
    });

    it('can be made unique', function(done) {
      Article.objects.join('author').unique().fetch().then(function() {
{'fraction_non_alphanumeric': 0.1863424289551817, 'fraction_numerical': 0.009260568358546585, 'mean_word_length': 2.733702823964107, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5610145', 'n_tokens_mistral': 4577, 'n_tokens_neox': 4355, 'n_words': 929}