/* eslint-disable max-lines */ describe('Tests for api', ()=>{ let api; let google; let model; let hbBrew; let googleBrew; let res; let modelBrew; let saveFunc; let markModifiedFunc; let saved; beforeEach(()=>{ jest.resetModules(); jest.restoreAllMocks(); saved = undefined; saveFunc = jest.fn(async function() { saved = { ...this, _id: '1' }; return saved; }); markModifiedFunc = jest.fn(()=>true); modelBrew = (brew)=>({ ...brew, save : saveFunc, markModified : markModifiedFunc, toObject : function() { delete this.save; delete this.toObject; delete this.markModified; return this; } }); google = require('./googleActions.js'); model = require('./homebrew.model.js').model; jest.mock('./googleActions.js'); google.authCheck = jest.fn(()=>'client'); google.newGoogleBrew = jest.fn(()=>'id'); google.deleteGoogleBrew = jest.fn(()=>true); jest.mock('./homebrew.model.js'); model.mockImplementation((brew)=>modelBrew(brew)); res = { status : jest.fn(()=>res), send : jest.fn(()=>{}), setHeader : jest.fn(()=>{}) }; api = require('./homebrew.api'); hbBrew = { text : `brew text`, style : 'hello yes i am css', title : 'some title', description : 'this is a description', tags : ['something', 'fun'], systems : ['D&D 5e'], lang : 'en', renderer : 'v3', theme : 'phb', published : true, authors : ['1', '2'], owner : '1', thumbnail : '', _id : 'mongoid', editId : 'abcdefg', shareId : 'hijklmnop', views : 1, lastViewed : new Date(), version : 1, pageCount : 1, textBin : '', views : 0 }; googleBrew = { ...hbBrew, googleId : '12345' }; }); describe('getId', ()=>{ it('should return only id if google id is not present', ()=>{ const { id, googleId } = api.getId({ params : { id : 'abcdefgh' } }); expect(id).toEqual('abcdefgh'); expect(googleId).toBeUndefined(); }); it('should return id and google id from request body', ()=>{ const { id, googleId } = api.getId({ params : { id : 'abcdefgh' }, body : { googleId : '12345' } }); expect(id).toEqual('abcdefgh'); expect(googleId).toEqual('12345'); }); it('should return 12-char id and google id from params', ()=>{ const { id, googleId } = api.getId({ params : { id : '123456789012345678901234567890123abcdefghijkl' } }); expect(googleId).toEqual('123456789012345678901234567890123'); expect(id).toEqual('abcdefghijkl'); }); it('should return 10-char id and google id from params', ()=>{ const { id, googleId } = api.getId({ params : { id : '123456789012345678901234567890123abcdefghij' } }); expect(googleId).toEqual('123456789012345678901234567890123'); expect(id).toEqual('abcdefghij'); }); }); describe('getBrew', ()=>{ const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew })); const notFoundError = { HBErrorCode: '05', message: 'Brew not found', name: 'BrewLoad Error', status: 404, accessType: 'share', brewId: '1' }; it('returns middleware', ()=>{ const getFn = api.getBrew('share'); expect(getFn).toBeInstanceOf(Function); }); it('should fetch from mongoose', async ()=>{ const testBrew = { title: 'test brew', authors: [] }; api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); model.get = jest.fn(()=>toBrewPromise(testBrew)); const fn = api.getBrew('share', true); const req = { brew: {} }; const next = jest.fn(); await fn(req, null, next); expect(req.brew).toEqual(testBrew); expect(next).toHaveBeenCalled(); expect(api.getId).toHaveBeenCalledWith(req); expect(model.get).toHaveBeenCalledWith({ shareId: '1' }); }); it('should handle mongoose error', async ()=>{ api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); model.get = jest.fn(()=>new Promise((_, rej)=>rej('Unable to find brew'))); const fn = api.getBrew('share', false); const req = { brew: {} }; const next = jest.fn(); let err; try { await fn(req, null, next); } catch (e) { err = e; } expect(err).toEqual(notFoundError); expect(req.brew).toEqual({}); expect(next).not.toHaveBeenCalled(); expect(api.getId).toHaveBeenCalledWith(req); expect(model.get).toHaveBeenCalledWith({ shareId: '1' }); }); it('changes tags from string to array', async ()=>{ const testBrew = { title: 'test brew', authors: [], tags: '' }; api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); model.get = jest.fn(()=>toBrewPromise(testBrew)); const fn = api.getBrew('share', true); const req = { brew: {} }; const next = jest.fn(); await fn(req, null, next); expect(req.brew.tags).toEqual([]); expect(next).toHaveBeenCalled(); }); it('throws if not logged in as author', async ()=>{ api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); model.get = jest.fn(()=>toBrewPromise({ title: 'test brew', authors: ['a'] })); const fn = api.getBrew('edit', true); const req = { brew: {} }; let err; try { await fn(req, null, null); } catch (e) { err = e; } expect(err).toEqual({ HBErrorCode: '04', message: 'User is not logged in', name: 'Access Error', status: 401, brewTitle: 'test brew', authors: ['a'] }); }); it('throws if logged in as invalid author', async ()=>{ api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); model.get = jest.fn(()=>toBrewPromise({ title: 'test brew', authors: ['a'] })); const fn = api.getBrew('edit', true); const req = { brew: {}, account: { username: 'b' } }; let err; try { await fn(req, null, null); } catch (e) { err = e; } expect(err).toEqual({ HBErrorCode: '03', message: 'User is not an Author', name: 'Access Error', status: 401, brewTitle: 'test brew', authors: ['a'] }); }); it('does not throw if no authors', async ()=>{ api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); model.get = jest.fn(()=>toBrewPromise({ title: 'test brew', authors: [] })); const fn = api.getBrew('edit', true); const req = { brew: {} }; const next = jest.fn(); await fn(req, null, next); expect(next).toHaveBeenCalled(); expect(req.brew.title).toEqual('test brew'); expect(req.brew.authors).toEqual([]); }); it('does not throw if valid author', async ()=>{ api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); model.get = jest.fn(()=>toBrewPromise({ title: 'test brew', authors: ['a'] })); const fn = api.getBrew('edit', true); const req = { brew: {}, account: { username: 'a' } }; const next = jest.fn(); await fn(req, null, next); expect(next).toHaveBeenCalled(); expect(req.brew.title).toEqual('test brew'); expect(req.brew.authors).toEqual(['a']); }); it('fetches google brew if needed', async()=>{ const stubBrew = { title: 'test brew', authors: ['a'] }; const googleBrew = { title: 'test google brew', text: 'brew text' }; api.getId = jest.fn(()=>({ id: '1', googleId: '2' })); model.get = jest.fn(()=>toBrewPromise(stubBrew)); google.getGoogleBrew = jest.fn(()=>new Promise((res)=>res(googleBrew))); const fn = api.getBrew('share', false); const req = { brew: {} }; const next = jest.fn(); await fn(req, null, next); expect(req.brew).toEqual({ title : 'test google brew', authors : ['a'], text : 'brew text', stubbed : true, description : '', editId : undefined, pageCount : 1, published : false, renderer : 'legacy', lang : 'en', shareId : undefined, systems : [], tags : [], theme : '5ePHB', thumbnail : '', textBin : undefined, version : undefined, createdAt : undefined, gDrive : false, style : undefined, trashed : false, updatedAt : undefined, views : 0 }); expect(next).toHaveBeenCalled(); expect(api.getId).toHaveBeenCalledWith(req); expect(model.get).toHaveBeenCalledWith({ shareId: '1' }); expect(google.getGoogleBrew).toHaveBeenCalledWith('2', '1', 'share'); }); it('access is denied to a locked brew', async()=>{ const lockBrew = { title: 'test brew', shareId: '1', lock: { locked: true, code: 404, shareMessage: 'brew locked' } }; model.get = jest.fn(()=>toBrewPromise(lockBrew)); api.getId = jest.fn(()=>({ id: '1', googleId: undefined })); const fn = api.getBrew('share', false); const req = { brew: {} }; const next = jest.fn(); await expect(fn(req, null, next)).rejects.toEqual({ 'HBErrorCode': '100', 'brewId': '1', 'brewTitle': 'test brew', 'code': 404, 'message': 'brew locked' }); }); }); describe('mergeBrewText', ()=>{ it('should set metadata and no style if it is not present', ()=>{ const result = api.mergeBrewText({ text : `brew`, title : 'some title', description : 'this is a description', tags : ['something', 'fun'], systems : ['D&D 5e'], renderer : 'v3', theme : 'phb', googleId : '12345' }); expect(result).toEqual(`\`\`\`metadata title: some title description: this is a description tags: - something - fun systems: - D&D 5e renderer: v3 theme: phb \`\`\` brew`); }); it('should set metadata and style', ()=>{ const result = api.mergeBrewText({ text : `brew`, style : 'hello yes i am css', title : 'some title', description : 'this is a description', tags : ['something', 'fun'], systems : ['D&D 5e'], renderer : 'v3', theme : 'phb', googleId : '12345' }); expect(result).toEqual(`\`\`\`metadata title: some title description: this is a description tags: - something - fun systems: - D&D 5e renderer: v3 theme: phb \`\`\` \`\`\`css hello yes i am css \`\`\` brew`); }); }); describe('exclusion methods', ()=>{ it('excludePropsFromUpdate removes the correct keys', ()=>{ const sent = Object.assign({}, googleBrew); const result = api.excludePropsFromUpdate(sent); expect(sent).toEqual(googleBrew); expect(result._id).toBeUndefined(); expect(result.views).toBeUndefined(); expect(result.lastViewed).toBeUndefined(); }); it('excludeGoogleProps removes the correct keys', ()=>{ const sent = Object.assign({}, googleBrew); const result = api.excludeGoogleProps(sent); expect(sent).toEqual(googleBrew); expect(result.tags).toBeUndefined(); expect(result.systems).toBeUndefined(); expect(result.published).toBeUndefined(); expect(result.authors).toBeUndefined(); expect(result.owner).toBeUndefined(); expect(result.views).toBeUndefined(); expect(result.thumbnail).toBeUndefined(); expect(result.version).toBeUndefined(); }); it('excludeStubProps removes the correct keys from the original object', ()=>{ const sent = Object.assign({}, googleBrew); const result = api.excludeStubProps(sent); expect(sent).not.toEqual(googleBrew); expect(result.text).toBeUndefined(); expect(result.textBin).toBeUndefined(); expect(result.renderer).toBeUndefined(); expect(result.pageCount).toBeUndefined(); }); }); describe('beforeNewSave', ()=>{ it('sets the title if none', ()=>{ const brew = { ...hbBrew, title : undefined }; api.beforeNewSave({}, brew); expect(brew.title).toEqual('brew text'); }); it('does not override the title if present', ()=>{ const brew = { ...hbBrew, title : 'test' }; api.beforeNewSave({}, brew); expect(brew.title).toEqual('test'); }); it('does not set authors if account missing username', ()=>{ api.beforeNewSave({}, hbBrew); expect(hbBrew.authors).toEqual([]); }); it('sets authors if account has username', ()=>{ api.beforeNewSave({ username: 'hi' }, hbBrew); expect(hbBrew.authors).toEqual(['hi']); }); it('merges brew text', ()=>{ api.mergeBrewText = jest.fn(()=>'merged'); api.beforeNewSave({}, hbBrew); expect(api.mergeBrewText).toHaveBeenCalled(); expect(hbBrew.text).toEqual('merged'); }); }); describe('newGoogleBrew', ()=>{ it('should call the correct methods', ()=>{ api.excludeGoogleProps = jest.fn(()=>'newBrew'); const acct = { username: 'test' }; const brew = { title: 'test title' }; api.newGoogleBrew(acct, brew, res); expect(google.authCheck).toHaveBeenCalledWith(acct, res); expect(api.excludeGoogleProps).toHaveBeenCalledWith(brew); expect(google.newGoogleBrew).toHaveBeenCalledWith('client', 'newBrew'); }); }); describe('newBrew', ()=>{ it('should set up a default brew via Homebrew model', async ()=>{ await api.newBrew({ body: { text: 'asdf' }, query: {}, account: { username: 'test user' } }, res); expect(res.status).toHaveBeenCalledWith(200); expect(res.send).toHaveBeenCalledWith({ _id : '1', authors : ['test user'], createdAt : undefined, description : '', editId : expect.any(String), gDrive : false, pageCount : 1, published : false, renderer : 'V3', lang : 'en', shareId : expect.any(String), style : undefined, systems : [], tags : [], text : undefined, textBin : expect.objectContaining({}), theme : '5ePHB', thumbnail : '', title : 'asdf', trashed : false, updatedAt : undefined, views : 0 }); }); it('should remove edit/share/google ids', async ()=>{ await api.newBrew({ body: { editId: '1234', shareId: '1234', googleId: '1234', text: 'asdf', title: '' }, query: {} }, res); expect(res.status).toHaveBeenCalledWith(200); expect(res.send).toHaveBeenCalled(); const sent = res.send.mock.calls[0][0]; expect(sent.editId).not.toEqual('1234'); expect(sent.shareId).not.toEqual('1234'); expect(sent.googleId).toBeUndefined(); }); it('should handle mongo error', async ()=>{ saveFunc = jest.fn(async function() { throw 'err'; }); model.mockImplementation((brew)=>modelBrew(brew)); let err; try { await api.newBrew({ body: { editId: '1234', shareId: '1234', googleId: '1234', text: 'asdf', title: '' }, query: {} }, res); } catch (e) { err = e; } expect(res.send).not.toHaveBeenCalled(); expect(err).not.toBeUndefined(); }); it('should save to google if requested', async()=>{ await api.newBrew({ body: { text: 'asdf', title: '' }, query: { saveToGoogle: true }, account: { username: 'test user' } }, res); expect(google.newGoogleBrew).toHaveBeenCalled(); expect(res.status).toHaveBeenCalledWith(200); expect(res.send).toHaveBeenCalledWith({ _id : '1', authors : ['test user'], createdAt : undefined, description : '', editId : expect.any(String), gDrive : false, pageCount : undefined, published : false, renderer : undefined, lang : 'en', shareId : expect.any(String), googleId : expect.any(String), style : undefined, systems : [], tags : [], text : undefined, textBin : undefined, theme : '5ePHB', thumbnail : '', title : 'asdf', trashed : false, updatedAt : undefined, views : 0 }); }); it('should handle google error', async()=>{ google.newGoogleBrew = jest.fn(()=>{ throw 'err'; }); await api.newBrew({ body: { text: 'asdf', title: '' }, query: { saveToGoogle: true }, account: { username: 'test user' } }, res); expect(res.status).toHaveBeenCalledWith(500); expect(res.send).toHaveBeenCalledWith('err'); }); }); describe('deleteGoogleBrew', ()=>{ it('should check auth and delete brew', async ()=>{ const result = await api.deleteGoogleBrew({ username: 'test user' }, 'id', 'editId', res); expect(result).toBe(true); expect(google.authCheck).toHaveBeenCalledWith({ username: 'test user' }, expect.objectContaining({})); expect(google.deleteGoogleBrew).toHaveBeenCalledWith('client', 'id', 'editId'); }); }); describe('Theme bundle', () => { it('should return Theme Bundle for a User Theme', async () => { const brews = { userThemeAID: { title: 'User Theme A', renderer: 'V3', theme: null, shareId: 'userThemeAID', style: 'User Theme A Style' } }; const toBrewPromise = (brew) => new Promise((res) => res({ toObject: () => brew })); model.get = jest.fn((getParams) => toBrewPromise(brews[getParams.shareId])); const req = { params: { renderer: "V3", id: "userThemeAID" }, get: () => { return 'localhost'; }, protocol: 'https' }; await api.getThemeBundle(req, res); expect(res.status).toHaveBeenCalledWith(200); expect(res.send).toHaveBeenCalledWith({ styles: ["/* From Brew: https://localhost/share/userThemeAID */\n\nUser Theme A Style"], snippets: [] }); }); it('should return Theme Bundle for nested User Themes', async () => { const brews = { userThemeAID: { title: 'User Theme A', renderer: 'V3', theme: 'userThemeBID', shareId: 'userThemeAID', style: 'User Theme A Style' }, userThemeBID: { title: 'User Theme B', renderer: 'V3', theme: 'userThemeCID', shareId: 'userThemeBID', style: 'User Theme B Style' }, userThemeCID: { title: 'User Theme C', renderer: 'V3', theme: null, shareId: 'userThemeCID', style: 'User Theme C Style' } }; const toBrewPromise = (brew) => new Promise((res) => res({ toObject: () => brew })); model.get = jest.fn((getParams) => toBrewPromise(brews[getParams.shareId])); const req = { params: { renderer: "V3", id: "userThemeAID" }, get: () => { return 'localhost'; }, protocol: 'https' }; await api.getThemeBundle(req, res); expect(res.status).toHaveBeenCalledWith(200); expect(res.send).toHaveBeenCalledWith({ styles: [ "/* From Brew: https://localhost/share/userThemeCID */\n\nUser Theme C Style", "/* From Brew: https://localhost/share/userThemeBID */\n\nUser Theme B Style", "/* From Brew: https://localhost/share/userThemeAID */\n\nUser Theme A Style" ], snippets: [] }); }); it('should return Theme Bundle for a Static Theme', async () => { const req = { params: { renderer: "V3", id: "5ePHB" }, get: () => { return 'localhost'; }, protocol: 'https' }; await api.getThemeBundle(req, res); expect(res.status).toHaveBeenCalledWith(200); expect(res.send).toHaveBeenCalledWith({ styles: [ `/* From Theme Blank */\n\n@import url("/themes/V3/Blank/style.css");`, `/* From Theme 5ePHB */\n\n@import url("/themes/V3/5ePHB/style.css");` ], snippets: [ "V3_Blank", "V3_5ePHB" ] }); }); it('should return Theme Bundle for nested User and Static Themes together', async () => { const brews = { userThemeAID: { title: 'User Theme A', renderer: 'V3', theme: 'userThemeBID', shareId: 'userThemeAID', style: 'User Theme A Style' }, userThemeBID: { title: 'User Theme B', renderer: 'V3', theme: 'userThemeCID', shareId: 'userThemeBID', style: 'User Theme B Style' }, userThemeCID: { title: 'User Theme C', renderer: 'V3', theme: '5eDMG', shareId: 'userThemeCID', style: 'User Theme C Style' } }; const toBrewPromise = (brew) => new Promise((res) => res({ toObject: () => brew })); model.get = jest.fn((getParams) => toBrewPromise(brews[getParams.shareId])); const req = { params: { renderer: "V3", id: "userThemeAID" }, get: () => { return 'localhost'; }, protocol: 'https' }; await api.getThemeBundle(req, res); expect(res.status).toHaveBeenCalledWith(200); expect(res.send).toHaveBeenCalledWith({ styles: [ `/* From Theme Blank */\n\n@import url("/themes/V3/Blank/style.css");`, `/* From Theme 5ePHB */\n\n@import url("/themes/V3/5ePHB/style.css");`, `/* From Theme 5eDMG */\n\n@import url("/themes/V3/5eDMG/style.css");`, "/* From Brew: https://localhost/share/userThemeCID */\n\nUser Theme C Style", "/* From Brew: https://localhost/share/userThemeBID */\n\nUser Theme B Style", "/* From Brew: https://localhost/share/userThemeAID */\n\nUser Theme A Style" ], snippets: [ "V3_Blank", "V3_5ePHB", "V3_5eDMG" ] }); }); it('should fail for an invalid Theme in the chain', async()=>{ const brews = { userThemeAID: { title: 'User Theme A', renderer: 'V3', theme: 'missingTheme', shareId: 'userThemeAID', style: 'User Theme A Style' }, }; const toBrewPromise = (brew) => new Promise((res) => res({ toObject: () => brew })); model.get = jest.fn((getParams) => toBrewPromise(brews[getParams.shareId])); const req = { params: { renderer: "V3", id: "userThemeAID" }, get: () => { return 'localhost'; }, protocol: 'https' }; await api.getThemeBundle(req, res); expect(res.status).toHaveBeenCalledWith(404); expect(res.send).toHaveBeenCalledWith('Theme Not Found - Renderer: V3, Name: missingTheme'); }); }); ////////////////////////////// describe('getBrewThemeWithStaticParent', ()=>{ it('should collect parent theme and brew style - returning as css with static parent imported.', async ()=>{ const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew })); model.get = jest.fn(()=>toBrewPromise({ title: 'test brew', renderer: 'V3', theme: '5eDMG', shareId: 'iAmAUserTheme', style: 'I Have a style!' })); const fn = api.getBrew('share', true); const req = { brew: {}, get: ()=>{return 'localhost';}, protocol: 'https' }; const next = jest.fn(); await fn(req, null, next); api.getBrewThemeCSS(req, res); const sent = res.send.mock.calls[0][0]; expect(sent).toBe(`@import url("/css/V3/5eDMG");\n\n/* From Brew: https://localhost/share/iAmAUserTheme */\n\nI Have a style!`); expect(res.status).toHaveBeenCalledWith(200); }); }); describe('getBrewThemeWithUserParent', ()=>{ it('should collect parent theme and brew style - returning as css with user-theme parent imported.', async ()=>{ const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew })); model.get = jest.fn(()=>toBrewPromise({ title: 'test brew', renderer: 'V3', shareId: 'iAmAUserTheme', theme: 'IamATheme', style: 'I Have a style!' })); const fn = api.getBrew('share', true); const req = { brew: {}, get: ()=>{return 'localhost';}, protocol: 'https' }; const next = jest.fn(); await fn(req, null, next); api.getBrewThemeCSS(req, res); const sent = res.send.mock.calls[0][0]; expect(sent).toBe(`@import url("/css/IamATheme");\n\n/* From Brew: https://localhost/share/iAmAUserTheme */\n\nI Have a style!`); expect(res.status).toHaveBeenCalledWith(200); }); }); describe('getStaticThemeCSS', ()=>{ it('should return an import of the theme including a parent.', async ()=>{ const req = { params : { renderer : 'V3', id : '5eDMG' } }; api.getStaticThemeCSS(req, res); const sent = res.send.mock.calls[0][0]; expect(sent).toBe('@import url("/css/V3/5ePHB");\n/* Static Theme 5e PHB */\n@import url("/themes/V3/5eDMG/style.css");\n/* Static Theme 5e DMG */\n'); expect(res.status).toHaveBeenCalledWith(200); }); it('should fail for an invalid static theme.', async()=>{ const req = { params : { renderer : 'V3', id : '5eDMGGGG' } }; api.getStaticThemeCSS(req, res); const sent = res.send.mock.calls[0][0]; expect(sent).toBe('Invalid Theme - Renderer: V3, Name: 5eDMGGGG'); expect(res.status).toHaveBeenCalledWith(404); }); }); //////////////////////////////// describe('deleteBrew', ()=>{ it('should handle case where fetching the brew returns an error', async ()=>{ api.getBrew = jest.fn(()=>async ()=>{ throw { message: 'err', HBErrorCode: '02' }; }); api.getId = jest.fn(()=>({ id: '1', googleId: '2' })); model.deleteOne = jest.fn(async ()=>{}); const next = jest.fn(()=>{}); await api.deleteBrew(null, null, next); expect(next).toHaveBeenCalled(); expect(model.deleteOne).toHaveBeenCalledWith({ editId: '1' }); }); it('should delete if no authors', async ()=>{ const brew = { ...hbBrew, authors : [] }; api.getBrew = jest.fn(()=>async (req)=>{ req.brew = brew; }); model.findOne = jest.fn(async ()=>modelBrew(brew)); model.deleteOne = jest.fn(async ()=>{}); const req = {}; await api.deleteBrew(req, res); expect(api.getBrew).toHaveBeenCalled(); expect(model.findOne).toHaveBeenCalled(); expect(model.deleteOne).toHaveBeenCalled(); }); it('should throw on delete error', async ()=>{ const brew = { ...hbBrew, authors : [] }; api.getBrew = jest.fn(()=>async (req)=>{ req.brew = brew; }); model.findOne = jest.fn(async ()=>modelBrew(brew)); model.deleteOne = jest.fn(async ()=>{ throw 'err'; }); const req = {}; let err; try { await api.deleteBrew(req, res); } catch (e) { err = e; } expect(err).not.toBeUndefined(); expect(api.getBrew).toHaveBeenCalled(); expect(model.findOne).toHaveBeenCalled(); expect(model.deleteOne).toHaveBeenCalled(); }); it('should delete when one author', async ()=>{ const brew = { ...hbBrew, authors : ['test'] }; api.getBrew = jest.fn(()=>async (req)=>{ req.brew = brew; }); model.findOne = jest.fn(async ()=>modelBrew(brew)); model.deleteOne = jest.fn(async ()=>{}); const req = { account: { username: 'test' } }; await api.deleteBrew(req, res); expect(api.getBrew).toHaveBeenCalled(); expect(model.findOne).toHaveBeenCalled(); expect(model.deleteOne).toHaveBeenCalled(); }); it('should remove one author when multiple present', async ()=>{ const brew = { ...hbBrew, authors : ['test', 'test2'] }; api.getBrew = jest.fn(()=>async (req)=>{ req.brew = brew; }); model.findOne = jest.fn(async ()=>modelBrew(brew)); model.deleteOne = jest.fn(async ()=>{}); const req = { account: { username: 'test' } }; await api.deleteBrew(req, res); expect(api.getBrew).toHaveBeenCalled(); expect(markModifiedFunc).toHaveBeenCalled(); expect(model.findOne).toHaveBeenCalled(); expect(model.deleteOne).not.toHaveBeenCalled(); expect(saveFunc).toHaveBeenCalled(); expect(saved.authors).toEqual(['test2']); }); it('should handle save error', async ()=>{ const brew = { ...hbBrew, authors : ['test', 'test2'] }; api.getBrew = jest.fn(()=>async (req)=>{ req.brew = brew; }); model.findOne = jest.fn(async ()=>modelBrew(brew)); model.deleteOne = jest.fn(async ()=>{}); saveFunc = jest.fn(async ()=>{ throw 'err'; }); const req = { account: { username: 'test' } }; let err; try { await api.deleteBrew(req, res); } catch (e) { err = e; } expect(err).not.toBeUndefined(); expect(api.getBrew).toHaveBeenCalled(); expect(model.findOne).toHaveBeenCalled(); expect(model.deleteOne).not.toHaveBeenCalled(); expect(saveFunc).toHaveBeenCalled(); }); it('should delete google brew', async ()=>{ const brew = { ...googleBrew, authors : ['test'] }; api.getBrew = jest.fn(()=>async (req)=>{ req.brew = brew; }); model.findOne = jest.fn(async ()=>modelBrew(brew)); model.deleteOne = jest.fn(async ()=>{}); api.deleteGoogleBrew = jest.fn(async ()=>true); const req = { account: { username: 'test' } }; await api.deleteBrew(req, res); expect(api.getBrew).toHaveBeenCalled(); expect(model.findOne).toHaveBeenCalled(); expect(model.deleteOne).toHaveBeenCalled(); expect(api.deleteGoogleBrew).toHaveBeenCalled(); }); it('should handle google brew delete error', async ()=>{ const brew = { ...googleBrew, authors : ['test'] }; api.getBrew = jest.fn(()=>async (req)=>{ req.brew = brew; }); model.findOne = jest.fn(async ()=>modelBrew(brew)); model.deleteOne = jest.fn(async ()=>{}); api.deleteGoogleBrew = jest.fn(async ()=>{ throw 'err'; }); const req = { account: { username: 'test' } }; await api.deleteBrew(req, res); expect(api.getBrew).toHaveBeenCalled(); expect(model.findOne).toHaveBeenCalled(); expect(model.deleteOne).toHaveBeenCalled(); expect(api.deleteGoogleBrew).toHaveBeenCalled(); }); it('should delete google brew and retain stub when multiple authors and owner request deletion', async ()=>{ const brew = { ...googleBrew, authors : ['test', 'test2'] }; api.getBrew = jest.fn(()=>async (req)=>{ req.brew = brew; }); model.findOne = jest.fn(async ()=>modelBrew(brew)); model.deleteOne = jest.fn(async ()=>{}); api.deleteGoogleBrew = jest.fn(async ()=>true); const req = { account: { username: 'test' } }; await api.deleteBrew(req, res); expect(api.getBrew).toHaveBeenCalled(); expect(markModifiedFunc).toHaveBeenCalled(); expect(model.findOne).toHaveBeenCalled(); expect(model.deleteOne).not.toHaveBeenCalled(); expect(api.deleteGoogleBrew).toHaveBeenCalled(); expect(saveFunc).toHaveBeenCalled(); expect(saved.authors).toEqual(['test2']); expect(saved.googleId).toEqual(undefined); expect(saved.text).toEqual(undefined); expect(saved.textBin).not.toEqual(undefined); }); it('should retain google brew and update stub when multiple authors and extra author requests deletion', async ()=>{ const brew = { ...googleBrew, authors : ['test', 'test2'] }; api.getBrew = jest.fn(()=>async (req)=>{ req.brew = brew; }); model.findOne = jest.fn(async ()=>modelBrew(brew)); model.deleteOne = jest.fn(async ()=>{}); api.deleteGoogleBrew = jest.fn(async ()=>true); const req = { account: { username: 'test2' } }; await api.deleteBrew(req, res); expect(api.getBrew).toHaveBeenCalled(); expect(model.findOne).toHaveBeenCalled(); expect(model.deleteOne).not.toHaveBeenCalled(); expect(api.deleteGoogleBrew).not.toHaveBeenCalled(); expect(saveFunc).toHaveBeenCalled(); expect(saved.authors).toEqual(['test']); expect(saved.googleId).toEqual(brew.googleId); }); }); });