UNPKG

smc-hub

Version:

CoCalc: Backend webserver component

812 lines (795 loc) 21.9 kB
// Generated by CoffeeScript 2.5.1 (function() { //######################################################################## // This file is part of CoCalc: Copyright © 2020 Sagemath, Inc. // License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details //######################################################################## /* TESTING of patches table COPYRIGHT : (c) 2017 SageMath, Inc. LICENSE : AGPLv3 */ var async, changefeed_series, create_accounts, create_projects, db, expect, misc, pgtest, setup, teardown; async = require('async'); expect = require('expect'); pgtest = require('./pgtest'); db = void 0; setup = function(cb) { return pgtest.setup(function(err) { db = pgtest.db; return cb(err); }); }; teardown = pgtest.teardown; ({create_accounts, create_projects, changefeed_series} = pgtest); misc = require('smc-util/misc'); describe('basic use of patches table from user -- ', function() { var accounts, patch0, path, projects, string_id, t0, t1, t2, t3; this.timeout(10000); before(setup); after(teardown); accounts = projects = string_id = void 0; path = 'a.txt'; it('creates 2 accounts', function(done) { return create_accounts(2, function(err, x) { accounts = x; return done(err); }); }); it('creates 2 projects', function(done) { return create_projects(2, accounts[0], function(err, x) { projects = x; return done(err); }); }); it('creates a syncstring', function(done) { string_id = db.sha1(projects[0], path); return db.user_query({ account_id: accounts[0], query: { syncstrings: { project_id: projects[0], path: path, users: accounts } }, cb: done }); }); t0 = misc.minutes_ago(10); patch0 = misc.to_json({ a: 'patch' }); it('user creates a patch', function(done) { return db.user_query({ account_id: accounts[0], query: { patches: { string_id: string_id, time: t0, user_id: 0, patch: patch0 } }, cb: done }); }); it('reads the patch back', function(done) { return db.user_query({ account_id: accounts[0], query: { patches: { string_id: string_id, time: t0, user_id: null, patch: null } }, cb: function(err, x) { expect(x).toEqual({ patches: { string_id: string_id, time: t0, user_id: 0, patch: patch0 } }); return done(err); } }); }); t1 = misc.minutes_ago(11); t2 = misc.minutes_ago(12); t3 = misc.minutes_ago(20); it('user creates a patch with all fields', function(done) { return db.user_query({ account_id: accounts[0], query: { patches: { string_id: string_id, time: t3, user_id: 1, patch: patch0, snapshot: 'foo', prev: t1, sent: t2 } }, cb: done }); }); it('reads the patch with all fields back', function(done) { return db.user_query({ account_id: accounts[0], query: { patches: { string_id: string_id, time: t3, user_id: 1, patch: null, snapshot: null, prev: null, sent: null } }, cb: function(err, x) { expect(x).toEqual({ patches: { string_id: string_id, time: t3, user_id: 1, patch: patch0, snapshot: 'foo', prev: t1, sent: t2 } }); return done(err); } }); }); it('reads all patches so far', function(done) { return db.user_query({ account_id: accounts[0], query: { patches: [ { string_id: string_id, time: null, user_id: null, patch: null } ] }, cb: function(err, x) { expect(x.patches.length).toEqual(2); return done(err); } }); }); it('reads only the more recent patch', function(done) { return db.user_query({ account_id: accounts[0], query: { patches: [ { string_id: string_id, time: { '>=': t0 }, user_id: null, patch: null } ] }, cb: function(err, x) { expect(x.patches.length).toEqual(1); expect(x.patches[0].time).toEqual(t0); return done(err); } }); }); return it('reads only the older patch', function(done) { return db.user_query({ account_id: accounts[0], query: { patches: [ { string_id: string_id, time: { '<': t0 }, user_id: null, patch: null } ] }, cb: function(err, x) { expect(x.patches.length).toEqual(1); expect(x.patches[0].time).toEqual(t3); return done(err); } }); }); }); describe('access control tests on patches table -- ', function() { var accounts, patch0, path, projects, string_id, t0; before(setup); after(teardown); // SETUP accounts = projects = string_id = void 0; path = 'a.txt'; it('creates 3 accounts', function(done) { return create_accounts(3, function(err, x) { accounts = x; return done(err); }); }); it('creates 2 projects', function(done) { return create_projects(2, accounts[0], function(err, x) { projects = x; return done(err); }); }); it('creates a syncstring', function(done) { string_id = db.sha1(projects[0], path); return db.user_query({ account_id: accounts[0], query: { syncstrings: { project_id: projects[0], path: path, users: accounts } }, cb: done }); }); t0 = misc.minutes_ago(10); patch0 = misc.to_json({ a: 'patch' }); it('creates a patch', function(done) { return db.user_query({ account_id: accounts[0], query: { patches: { string_id: string_id, time: t0, user_id: 0, patch: patch0 } }, cb: done }); }); it('tries to read as anon to patches table and fails', function(done) { return db.user_query({ query: { patches: { string_id: null, time: null, user_id: null, patch: null } }, cb: function(err) { expect(err).toEqual("FATAL: anonymous get queries not allowed for table 'patches'"); return done(); } }); }); it('tries to write as anon to patches table and fails', function(done) { return db.user_query({ query: { patches: { string_id: string_id, time: new Date(), user_id: 0, patch: patch0 } }, cb: function(err) { expect(err).toEqual("FATAL: no anonymous set queries"); return done(); } }); }); it('tries to write as user not on the project and fails', function(done) { return db.user_query({ account_id: accounts[1], query: { patches: { string_id: string_id, time: new Date(), user_id: 0, patch: patch0 } }, cb: function(err) { expect(err).toEqual("FATAL: user must be an admin"); return done(); } }); }); it('tries to write as different project and fails', function(done) { return db.user_query({ project_id: projects[1], query: { patches: { string_id: string_id, time: new Date(), user_id: 0, patch: patch0 } }, cb: function(err) { expect(err).toEqual("FATAL: project not allowed to write to syncstring in different project"); return done(); } }); }); it('makes account1 an admin', function(done) { return db.make_user_admin({ account_id: accounts[1], cb: done }); }); it('tries to write as admin and succeeds', function(done) { return db.user_query({ account_id: accounts[1], query: { patches: { string_id: string_id, time: misc.minutes_ago(2), user_id: 0, patch: patch0 } }, cb: done }); }); it('makes account2 a collab', function(done) { return db.add_user_to_project({ project_id: projects[0], account_id: accounts[2], cb: done }); }); it('tries to write as collab and succeeds', function(done) { return db.user_query({ account_id: accounts[2], query: { patches: { string_id: string_id, time: misc.minutes_ago(3), user_id: 0, patch: patch0 } }, cb: done }); }); it('tries to write as same project and succeeds', function(done) { return db.user_query({ project_id: projects[0], query: { patches: { string_id: string_id, time: misc.minutes_ago(1), user_id: 0, patch: patch0 } }, cb: function(err) { return done(err); } }); }); /* * NOTE: I removed this constraint, since code handles the undefined case fine, * and it was causing problems. We should revisit this later. it 'tries to write negative user number and fails', (done) -> db.user_query project_id : projects[0] query : {patches:{string_id:string_id, time:misc.minutes_ago(4), user_id:-1, patch:patch0}} cb : (err) -> expect(err).toContain('new row for relation "patches" violates check constraint') done() it 'tries to write without including user field at all (and fails)', (done) -> db.user_query account_id : accounts[1] query : {patches:{string_id:string_id, time:t0, patch:patch0}} cb : (err) -> expect(err).toContain('null value in column "user_id" violates not-null constraint') done() */ it('tries to write invalid string_id and fails', function(done) { return db.user_query({ project_id: projects[0], query: { patches: { string_id: 'sage', time: misc.minutes_ago(4), user_id: 0, patch: patch0 } }, cb: function(err) { expect(err).toEqual("FATAL: string_id (='sage') must be a string of length 40"); return done(); } }); }); it('tries to write invalid time and fails', function(done) { return db.user_query({ project_id: projects[0], query: { patches: { string_id: string_id, time: 'sage', user_id: 0, patch: patch0 } }, cb: function(err) { expect(err).toContain('invalid input syntax for type timestamp'); return done(); } }); }); it('tries to write invalid sent type and fails', function(done) { return db.user_query({ project_id: projects[0], query: { patches: { string_id: string_id, time: misc.minutes_ago(4), user_id: 0, sent: 'sage', patch: patch0 } }, cb: function(err) { expect(err).toContain('invalid input syntax for type timestamp'); return done(); } }); }); it('tries to write invalid prev type and fails', function(done) { return db.user_query({ project_id: projects[0], query: { patches: { string_id: string_id, time: misc.minutes_ago(4), user_id: 0, prev: 'sage', patch: patch0 } }, cb: function(err) { expect(err).toContain('invalid input syntax for type timestamp'); return done(); } }); }); it('tries to change past author and fails', function(done) { return db.user_query({ account_id: accounts[1], query: { patches: { string_id: string_id, time: t0, user_id: 1, patch: patch0 } }, cb: function(err) { expect(err).toEqual('you may not change the author of a patch from 0 to 1'); return done(); } }); }); it('tries to write without including time field at all (and fails)', function(done) { return db.user_query({ account_id: accounts[1], query: { patches: { string_id: string_id, user_id: 1, patch: patch0 } }, cb: function(err) { expect(`${err}`).toEqual("FATAL: query must specify (primary) key 'time'"); return done(); } }); }); return it('tries to write without including string field at all (and fails)', function(done) { return db.user_query({ account_id: accounts[1], query: { patches: { time: t0, user_id: 1, patch: patch0 } }, cb: function(err) { expect(err).toEqual("FATAL: string_id (='undefined') must be a string of length 40"); return done(); } }); }); }); describe('changefeed tests on patches table', function() { var accounts, i, patch0, path, projects, string_id, t; before(setup); after(teardown); accounts = projects = string_id = void 0; path = 'a.txt'; t = (function() { var j, results; results = []; for (i = j = 0; j < 10; i = ++j) { results.push(misc.minutes_ago(10 - i)); } return results; })(); patch0 = misc.to_json({ a: 'patch' }); it('creates 2 accounts', function(done) { return create_accounts(2, function(err, x) { accounts = x; return done(err); }); }); it('creates 1 projects', function(done) { return create_projects(1, accounts[0], function(err, x) { projects = x; return done(err); }); }); it('creates a syncstring', function(done) { string_id = db.sha1(projects[0], path); return db.user_query({ account_id: accounts[0], query: { syncstrings: { project_id: projects[0], path: path, users: accounts } }, cb: done }); }); it('creates a changefeed as user', function(done) { var changefeed_id; changefeed_id = misc.uuid(); return db.user_query({ account_id: accounts[0], query: { patches: [ { string_id: string_id, time: null, user_id: null, patch: null } ] }, changes: changefeed_id, cb: changefeed_series([ function(x, cb) { var ref; expect(x != null ? (ref = x.patches) != null ? ref.length : void 0 : void 0).toEqual(0); // insert a new patch return db.user_query({ account_id: accounts[0], query: { patches: { string_id: string_id, time: t[0], user_id: 0, patch: patch0 } }, cb: cb }); }, function(x, cb) { expect(x).toEqual({ action: 'insert', new_val: { string_id: string_id, time: t[0], user_id: 0, patch: patch0 } }); // modify the just-inserted patch -- should not fire anything off since sent isn't a field we're watching return db.user_query({ account_id: accounts[0], query: { patches: { string_id: string_id, time: t[0], user_id: 0, patch: patch0, sent: t[1] } }, cb: function(err) { if (err) { return cb(err); } else { // insert new patch return db.user_query({ account_id: accounts[0], query: { patches: { string_id: string_id, time: t[2], user_id: 0, patch: 'foo' } }, cb: cb }); } } }); }, function(x, cb) { expect(x).toEqual({ action: 'insert', new_val: { string_id: string_id, time: t[2], user_id: 0, patch: 'foo' } }); return db.user_query_cancel_changefeed({ id: changefeed_id, cb: cb }); }, function(x, cb) { expect(x).toEqual({ action: 'close' }); return cb(); } ], done) }); }); return it('creates a changefeed as project', function(done) { var changefeed_id; changefeed_id = misc.uuid(); return db.user_query({ project_id: projects[0], query: { patches: [ { string_id: string_id, time: { '>=': t[2] }, user_id: null, patch: null, sent: null } ] }, changes: changefeed_id, cb: changefeed_series([ function(x, cb) { var ref; expect(x != null ? (ref = x.patches) != null ? ref.length : void 0 : void 0).toEqual(1); // insert a new enough patch to notice return db.user_query({ account_id: accounts[0], query: { patches: { string_id: string_id, time: t[3], user_id: 1, patch: patch0 } }, cb: cb }); }, function(x, cb) { expect(x).toEqual({ action: 'insert', new_val: { string_id: string_id, time: t[3], user_id: 1, patch: patch0 } }); // modify sent part of the just-inserted patch -- should fire since we *are* watching sent column return db.user_query({ account_id: accounts[0], query: { patches: { string_id: string_id, time: t[3], user_id: 1, sent: t[1] } }, cb: cb }); }, function(x, cb) { expect(x.action).toEqual('update'); expect(x.new_val).toEqual({ string_id: string_id, time: t[3], sent: t[1] }); return db._query({ query: "DELETE FROM patches", where: { string_id: string_id, time: t[0] }, cb: function(err) { if (err) { return cb(err); } else { // delete newer patch -- should fire changefeed return db._query({ query: "DELETE FROM patches", where: { string_id: string_id, time: t[3] }, cb: cb }); } } }); }, function(x, cb) { expect(x).toEqual({ action: 'delete', old_val: { string_id: string_id, time: t[3] } }); return db.user_query_cancel_changefeed({ id: changefeed_id, cb: cb }); }, function(x, cb) { expect(x).toEqual({ action: 'close' }); return cb(); } ], done) }); }); }); }).call(this); //# sourceMappingURL=postgres-user-queries-patches.js.map