//region imports import {Reactive} from "../decorators/reactive/reactive-decorator"; import {Mongo} from "../db/__mock__/mongo"; import {field} from "../decorators/field/field-decorator"; import {Model, PartialModel} from "../model/Model"; import {hasOne} from "../decorators/has-one/has-one-decorator"; import {hasMany} from "../decorators/has-many/has-many-decorator"; import Mock = jest.Mock; //endregion @Reactive() export class Person extends Model { @field name: string @field age: number } describe('Relations', () => { const mongo = new Mongo(), upsert_mock = mongo.upsert, upsert_calls = upsert_mock.mock.calls; beforeEach(async () => { await Reactive.init({db_config: {username: 'blah', pwd: 'Blah', mongo_instance: mongo}}) }); afterEach(async () => { upsert_mock.mockClear(); }) test('hasOne sanity with same-class child', () => { @Reactive() class Person extends Model { @field name @hasOne child: Person } const key = 'child', child = new Person({name: 'child'}), parent = new Person({name: 'parent', child}) expect(parent._hasOnes[key] === child._id) expect((child)._parents[0].key === key) expect((child)._parents[0]._id === child._id) expect((child)._parents[0].collection_name === Person.collection_name) expect(parent.child._id === child._id) expect(upsert_mock).toHaveBeenCalledWith({_id: child._id}, expect.objectContaining({name: 'child'}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: parent._id}, expect.objectContaining({name: 'parent'}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: parent._id}, expect.objectContaining(>>{_hasOnes: {[key]: child._id}}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: child._id}, expect.objectContaining(>>{ _parents: [{ _id : parent._id, collection_name: Person.collection_name, key }] }), Person.collection_name) }) test('hasOne sanity with different-class child', () => { @Reactive() class Dog extends Model { @field name: string } @Reactive() class Person extends Model { @field name: string @hasOne dog: Dog } const name = 'John', key = 'dog', dog = new Dog({name: 'Sparky',}), person = new Person({name, dog}); expect(person._hasOnes[key] === dog._id) expect(Person.hasOnes[key]).toBeTruthy(); expect(Person.hasOnes[key]).toBe(Dog); expect((dog)._parents[0].key === key) expect((dog)._parents[0]._id === person._id) expect((dog)._parents[0].collection_name === Person.collection_name) expect(person.dog._id === dog._id) expect(upsert_mock).toHaveBeenCalledWith({_id: person._id}, expect.objectContaining({name: 'John'}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: dog._id}, expect.objectContaining({name: 'Sparky'}), Dog.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: person._id}, expect.objectContaining(>>{_hasOnes: {[key]: dog._id}}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: dog._id}, >>{ _parents: [ { _id : person._id, collection_name: Person.collection_name, key : 'dog' } ] }, Dog.collection_name) }) test('hasMany sanity', () => { @Reactive() class Cat extends Model { @field name } @Reactive() class Person extends Model { @field name @hasMany cats: Cat[] } const key = 'cats', cat1 = new Cat({name: 'Fluffy'}), cat2 = new Cat({name: 'Skinny'}), person = new Person({name: 'John'}); person.cats = [cat1] person.cats.push(cat2) expect(person._hasManys[key]).toContain(cat1._id) expect(person._hasManys[key]).toContain(cat2._id) expect(Person.hasManys).toEqual({cats: Array}); expect((cat1)._parents[0]).toEqual({ _id : person._id, key, collection_name: Person.collection_name }) expect((cat2)._parents[0]).toEqual({ _id : person._id, key, collection_name: Person.collection_name }) expect(person.cats[0]._id === cat1._id) expect(person.cats[1]._id === cat2._id) expect(upsert_mock).toHaveBeenCalledWith({_id: cat1._id}, expect.objectContaining({name: 'Fluffy'}), Cat.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: cat2._id}, expect.objectContaining({name: 'Skinny'}), Cat.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: person._id}, expect.objectContaining({name: 'John'}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: person._id}, expect.objectContaining(>>{_hasManys: {cats: [cat1._id, cat2._id]}}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: cat1._id}, expect.objectContaining(>>{ _parents: [{ _id : person._id, collection_name: Person.collection_name, key : 'cats' }] }), Cat.collection_name) }) test('remove hasOne relation', () => { @Reactive() class Person extends Model { @field name @hasOne child: Person } const key = 'child', child = new Person({name: 'child'}), parent = new Person({name: 'parent', child}); parent[key] = undefined expect(parent[key]).toBeNull() expect(parent._hasOnes[key]).toBeUndefined() expect((child)._parents.find(p => p._id === child._id)).toBeUndefined() expect(upsert_mock).toHaveBeenCalledWith({_id: child._id}, expect.objectContaining({name: 'child'}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: parent._id}, expect.objectContaining({name: 'parent'}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: parent._id}, expect.objectContaining({_hasOnes: {[key]: child._id}}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: parent._id}, expect.objectContaining({_hasOnes: {}}), Person.collection_name) }) test('remove hasMany relation with [] assignments', () => { @Reactive() class Cat extends Model { @field name // @hasOne person: Person } @Reactive() class Person extends Model { @field name @hasMany cats: Cat[] } const cat1 = new Cat({name: 'Mitzy'}), person = new Person({name: 'person', cats: [cat1]}); person.cats = [] expect(person.cats).toEqual([]) expect((cat1)._parents.length).toEqual(0) expect((cat1)._parents.find(p => p._id === person._id)).toBeUndefined() expect(upsert_mock).toHaveBeenCalledWith({_id: person._id}, expect.objectContaining({_hasManys: {"cats": [cat1._id]}}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: person._id}, {_hasManys: {"cats": []}}, Person.collection_name) }) test('remove hasMany relation with pop() or length', () => { @Reactive() class Cat extends Model { @field name } @Reactive() class Person extends Model { @field name @hasMany cats: Cat[] } const cat1 = new Cat({name: 'Mitzy'}), person = new Person({name: 'person', cats: [cat1]}); // person.cats.pop() person.cats.length = 0; expect(person.cats).toEqual([]) expect((cat1)._parents.length).toEqual(0) expect((cat1)._parents.find(p => p._id === person._id)).toBeUndefined() expect(upsert_mock).toHaveBeenCalledWith({_id: person._id}, expect.objectContaining({_hasManys: {"cats": [cat1._id]}}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: person._id}, {_hasManys: {"cats": []}}, Person.collection_name) }) test('remove some hasMany relations from the array with length or splice', () => { @Reactive() class Cat extends Model { @field name } @Reactive() class Person extends Model { @field name @hasMany cats: Cat[] } const cat1 = new Cat({name: 'Cat1'}), cat2 = new Cat({name: 'Cat2'}), cat3 = new Cat({name: 'Cat3'}), person = new Person({name: 'person', cats: [cat1, cat2, cat3]}); expect(person.cats).toEqual([cat1, cat2, cat3]) person.cats = [cat1]; expect(person.cats).toEqual([cat1]) expect((cat1)._parents.length).toEqual(1) expect((cat2)._parents.length).toEqual(0) expect((cat3)._parents.length).toEqual(0) expect((cat1)._parents.find(p => p._id === person._id)).toBeTruthy() expect((cat2)._parents.find(p => p._id === person._id)).toBeUndefined() expect((cat3)._parents.find(p => p._id === person._id)).toBeUndefined() expect(upsert_mock).toHaveBeenCalledWith({_id: person._id}, expect.objectContaining({_hasManys: {cats: [cat1._id, cat2._id, cat3._id]}}), Person.collection_name) expect(upsert_mock).toHaveBeenCalledWith({_id: person._id}, {_hasManys: {"cats": [cat1._id]}}, Person.collection_name) const cat4 = new Cat({name: 'Cat4'}); person.cats.push(cat4) expect(person.cats).toEqual([cat1, cat4]) expect(upsert_mock).toHaveBeenCalledWith({_id: person._id}, expect.objectContaining({_hasManys: {cats: [cat1._id, cat4._id]}}), Person.collection_name) expect((cat1)._parents.find(p => p._id === person._id)).toBeTruthy() expect((cat4)._parents.find(p => p._id === person._id)).toBeTruthy() }) });