<commit_before>// Copyright (c) 2009-2010, <NAME>
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the author nor the names of contributors may be
//       used to endorse or promote products derived from this software
//       without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// 'with' is used here in order to ensure that namespaces are used correctly
with (require('index')) {

  var inner = require('inner');
  var core = inner.core;


  // May be this function should be in utils.
  function thrower (error) {
    return function () {
      throw error;
    };
  }

  //////////////////////////////////////////////////////////////////////////////
  // unittest tests
  //////////////////////////////////////////////////////////////////////////////

  exports.UnittestTestCase = TestCase.subclass(
    {
      name: 'unittest',

      testTestResult: function () {
        var tr = new TestResult();
        assert(tr.wasSuccessful());
        tr.startTest(1);
        tr.addSuccess(1);
        assert(tr.wasSuccessful());
        tr.startTest(2);
        tr.addError(2, 'error');
        tr.startTest(3);
        tr.addFailure(3, 'failure');
        assertSame(tr.testsRun, 3);
        assertEqual(tr.errors, [[2, 'error']]);
        assertEqual(tr.failures, [[3, 'failure']]);
        assert(!tr.wasSuccessful());
      },

      testTestCase: function () {
        var tested = false, setUp = false, tearedDown = false;
        var started = 0, stopped = 0;
        var tr = new TestResult();
        tr.startTest = function () { ++started; };
        tr.stopTest = function () { ++stopped; };

        var TC = TestCase.subclass(
          {
            setUp: function () {
              setUp = true;
            },
            tearDown: function () {
              tearedDown = true;
            },
            f: function () {
              tested = true;
            }
          });

        function run(TC) {
          new TC('f').run(tr);
        }


        var tc = new TC('f');
        assertSame(tc.countTestCases(), 1);
        assertSame(tc + '', 'f');
        tc.name = 'test';
        assertSame(repr(tc), '<TestCase f(test)>');

        run(TC);
        assert(tested && setUp && tearedDown);
        tested = setUp = tearedDown = false;

        var BadSetUpTC = TC.subclass({setUp: thrower(1)});
        run(BadSetUpTC);
        assert(!tested && !setUp && !tearedDown);

        var ErrorTC = TC.subclass({f: thrower(2)});
        run(ErrorTC);
        assert(!tested && setUp && tearedDown);
        setUp = tearedDown = false;

        var assertionError = new AssertionError(3);
        var FailureTC = TC.subclass({f: thrower(assertionError)});
        run(FailureTC);
        assert(!tested && setUp && tearedDown);
        setUp = tearedDown = false;

        var BadTearDownTC = TC.subclass({tearDown: thrower(4)});
        run(BadTearDownTC);
        assert(tested && setUp && !tearedDown);

        assertEqual(tr.errors.map(function (error) { return error[1]; }),
                    [1, 2, 4]);
        assertEqual(tr.failures.map(function (failure) { return failure[1]; }),
                    [assertionError]);
        assertSame(started, stopped);
        assertSame(started, 5);
      },

      testTestSuite: function () {
        var TC = TestCase.subclass({f: function () {}, g: function () {}});
        var ts = new TestSuite([new TC('f'), new TC('g')]);
        ts.addTest(new TC('f'));
        assertSame(ts.countTestCases(), 3);
        assertSame(repr(ts), '<TestSuite f, g, f>');
        var tr = new TestResult();
        ts.run(tr);
        assertSame(tr.testsRun, 3);
      },

      testStreamTestResult: function () {
        var stream = new Stream();
        var ttr = new StreamTestResult(stream);
        ttr.startTest('hi');
        ttr.addError();
        ttr.addFailure();
        ttr.addSuccess();
        assertSame(stream.read(), 'hi ERROR\n FAIL\n ok\n');
      },

      testLoadTestSuite: function () {
        var ts = new TestSuite();
        assertSame(loadTestSuite(ts), ts);
        var TC = TestCase.subclass(
          {
            name: 'TC',
            test1: function () {},
            test2: function () {},
            func: function () {},
            test3: 42
          });
        assertSame(repr(loadTestSuite(new TC('test1'))),
                   '<TestSuite test1(TC)>');
        assertSame(repr(loadTestSuite(TC)),
                   '<TestSuite test1(TC), test2(TC)>');
        var m = {
          ts: new TestSuite([new TC('func')]),
          tc: new TC('test1'),
          TC: TC
        };
        assertSame(loadTestSuite(m) + '',
                   'func(TC), test1(TC), test1(TC), test2(TC)');
        assertSame(loadTestSuite([m, new TC('test2')]) + '',
                   'func(TC), test1(TC), test1(TC), test2(TC), test2(TC)');
        assertThrow(TypeError, loadTestSuite, 42);
      },

      testRunTestViaStream: function () {
        var TC = TestCase.subclass(
          {
            name: 'test',
            testOk: function () {},
            testError: thrower(1),
            testAssert: partial(assert, false, 'msg1'),
            testAssertEqual: partial(assertEqual, 1, 2, 'msg2'),
            testAssertThrow: partial(assertThrow, Error, thrower(1))
          });
        var stream = new Stream();
        runTestViaStream(loadTestSuite(TC), stream);
        assert(stream.read().startsWith(
                 'testOk(test) ok\n' +
                 'testError(test) ERROR\n' +
                 'testAssert(test) FAIL\n' +
                 'testAssertEqual(test) FAIL\n' +
                 'testAssertThrow(test) FAIL\n' +
                 '=====\n'),
               'TextTestRunner');
      },

      testTestClient: function () {
        var users = ['user1', 'user2'];
        var apps = {
          app1: {
            admin: 'user1'
          },
          app2: {
            admin: 'user2',
            developers: ['user1']
          },
          app3: {
            admin: 'user1'
          },
          app4: {
            admin: 'user2',
            developers: ['user1']
          }
        };
        var client = new TestClient(users, apps);
        var aspect = weave(
          InsteadOf, require.main.exports, 'main',
          function (request) { return eval(request.data + ''); });
        function request(data) {
          return client.request({data: data});
        }

        client.login('no such user');
        client.login('user1');
        assertSame(client.get(), undefined);
        assertSame(request('request.user'), 'user1');
        assertSame(client.request({user: 'user2', data: 'request.user'}),
                   'user2');
        client.logout();
        assertSame(request('request.user'), '');
        assertSame(request('request.method'), 'get');
        assertSame(request('request.path'), '/');
        assertSame(request('typeof(request.get)'), 'object');
        assertSame(request('typeof(request.post)'), 'object');
        assertSame(request('typeof(request.headers)'), 'object');
        assertSame(request('typeof(request.files)'), 'object');

        assertSame(client.get({data: 'request.method'}), 'get');
        assertSame(client.put({data: 'request.method'}), 'put');
        assertSame(client.post({data: 'request.method'}), 'post');
        assertSame(client.del({data: 'request.method'}), 'delete');

        assertEqual(items(request('getAppDescription("app1")')),
                    [
                      ['admin', 'user1'],
                      ['developers', []],
                      ['summary', ''],
                      ['description', ''],
                      ['labels', []],
                      ['name', 'app1']
                    ]);
        assertEqual(request('getAdminedApps("user1")'), ['app1', 'app3']);
        assertEqual(request('getDevelopedApps("user1")'), ['app2', 'app4']);
        assertEqual(request('getDevelopedApps("user2")'), []);
        assertThrow(
          NoSuchUserError, request, 'getAdminedApps("no such user")');
        assertThrow(
          NoSuchUserError, request, 'getDevelopedApps("no such user")');

        var context = {};
        assertSame(
          request(
            'new Response(new Template("").render(context))').context,
          context);

        var H = Handler.subclass(
          {
            get: function () { return {}; }
          });
        assertSame(request('new H().handle({method: "get"})').handler, H);

        aspect.unweave();
      }
    });

  //////////////////////////////////////////////////////////////////////////////
  // core tests
  //////////////////////////////////////////////////////////////////////////////

  exports.CoreTestCase = TestCase.subclass(
    {
      name: 'core',

      setUp: function () {
        this._clean();
        db.create('X', {n: db.number, b: db.bool, s: db.string});
        db.insert('X', {n: 0, b: false, s: 'zero'});
        db.insert('X', {n: 1, b: false, s: 'one'});
        db.insert('X', {n: 42, b: true, s: 'the answer'});
      },

      tearDown: function () {
        this._clean();
      },

      _clean: function () {
        fs.list('').forEach(fs.remove);
        db.drop(db.list());
      },

      testQuery: function () {
        assert(db.query('X') instanceof Array);
        assertEqual(db.query('X where !b').length, 2);
        assertEqual(db.query('X.n where !b', [], 'n').map(items),
                    [[['n', 0]], [['n', 1]]]);
        assertEqual(
          db.query('X[b, n]', [], ['b', 'n * $'], [-1]).map(items),
          [
            [['b', false], ['n', 1]],
            [['b', false], ['n', 0]],
            [['b', true], ['n', 42]]
          ]);
        assertEqual(
          db.query('X.b where n > $1 && s != $2', [0, 'one']).map(items),
          [[['b', true]]]);
        assertEqual(
          db.query('X.n', [], 'n', [], 1, 1).map(items),
          [[['n', 1]]]);
      },

      testCount: function () {
        assertSame(db.count('X'), 3);
        assertSame(db.count('X.b where n > $1 && s != $2', [0, 'one']), 1);
      },

      testCreate: function () {
        db.create('Y', {});
        assertEqual(items(db.getHeader('Y')), []);
        assertEqual(items(db.getHeader('X')).sort(),
                    [['b', 'bool'], ['n', 'number'], ['s', 'string']]);
        db.drop(['Y']);
        db.create('Y',
                  {
                    s: db.number.serial(),
                    i: db.number.integer(),
                    u: db.string.unique(),
                    f: db.string.foreign('Y', 'u'),
                    c: db.number.check('c % 2 == 0'),
                    s1: db.number.integer(),
                    i1: db.number.integer(),
                    d: db.number.default_('15')
                  },
                  {
                    unique: [['s', 'i']],
                    foreign: [[['s1', 'i1'], 'Y', ['s', 'i']]],
                    check: ['s != i']
                  });
        assertEqual(
          db.getForeign('Y').sort(),
          [[['f'], 'Y', ['u']], [['s1', 'i1'], 'Y', ['s', 'i']]]);
        assertEqual(db.getUnique('Y').sort(), [['s', 'i'], ['u']]);
        assertEqual(db.getSerial('Y'), ['s']);
        assertEqual(db.getInteger('Y').sort(), ['i', 'i1', 's', 's1']);
        assertEqual(items(db.getDefault('Y')), [['d', 15]]);
        assertEqual(db.list(), ['X', 'Y']);
        db.drop(['Y']);
        assertEqual(db.list(), ['X']);
      },

      testDrop: function () {
        db.create('Y', {u: db.number.unique()});
        db.create('Z', {f: db.number.foreign('Y', 'u')});
        assertThrow(RelVarDependencyError, db.drop, ['Y']);
        db.drop(['Y', 'Z']);
      },

      testConstruct: function () {
        function C() {
          this.args = Array.slice(arguments);
        }
        assertEqual(construct(C, [1, 2]).args, [1, 2]);
        assertEqual(construct(C, {0: 1, 1: 2, length: 2}).args, [1, 2]);
      },

      testDescribeApp: function () {
        var description = getAppDescription('ak');
        assertSame(description.name, 'ak');
      },

      testRemove: function () {
        fs.createDir('dir');
        fs.createDir('dir/subdir');
        fs.write('file', 'hello');
        fs.write('dir/subdir/f', 'hi');
        fs.remove('dir');
        fs.remove('file');
        assertEqual(fs.list(''), [], 'fs.remove');
      },

      testRequestApp: function() {
        var oldRequestApp = _core.requestApp;
        _core.requestApp = function (appName, request, files, data) {
          return new Binary(
            [
              data,
              '',
              repr(JSON.parse(request)[appName])
            ].concat(files).join('\n'));
        };

        var response = requestApp('method', {data: '201'});
        assertSame(response.status, 201);
        assertSame(response.content + '', '"get"');
        assertEqual(items(response.headers), []);
        assertSame(requestApp('path', {data: '200'}).content + '', '"/"');
        assertSame(requestApp('path', {data: '200', path: 'a/b'}).content + '',
                   '"/a/b"');
        assertSame(requestApp('post', {data: '200', post: 'hi'}).content + '',
                   '"hi"');
        assertSame(requestApp('get', {data: '200'}).content + '', '{}');
        assertSame(requestApp('fileNames',<commit_msg>Added testRead.<commit_after>// Copyright (c) 2009-2010, <NAME>
// All rights reserved.

// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the author nor the names of contributors may be
//       used to endorse or promote products derived from this software
//       without specific prior written permission.

// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// 'with' is used here in order to ensure that namespaces are used correctly
with (require('index')) {

  var inner = require('inner');
  var core = inner.core;


  // May be this function should be in utils.
  function thrower (error) {
    return function () {
      throw error;
    };
  }

  //////////////////////////////////////////////////////////////////////////////
  // unittest tests
  //////////////////////////////////////////////////////////////////////////////

  exports.UnittestTestCase = TestCase.subclass(
    {
      name: 'unittest',

      testTestResult: function () {
        var tr = new TestResult();
        assert(tr.wasSuccessful());
        tr.startTest(1);
        tr.addSuccess(1);
        assert(tr.wasSuccessful());
        tr.startTest(2);
        tr.addError(2, 'error');
        tr.startTest(3);
        tr.addFailure(3, 'failure');
        assertSame(tr.testsRun, 3);
        assertEqual(tr.errors, [[2, 'error']]);
        assertEqual(tr.failures, [[3, 'failure']]);
        assert(!tr.wasSuccessful());
      },

      testTestCase: function () {
        var tested = false, setUp = false, tearedDown = false;
        var started = 0, stopped = 0;
        var tr = new TestResult();
        tr.startTest = function () { ++started; };
        tr.stopTest = function () { ++stopped; };

        var TC = TestCase.subclass(
          {
            setUp: function () {
              setUp = true;
            },
            tearDown: function () {
              tearedDown = true;
            },
            f: function () {
              tested = true;
            }
          });

        function run(TC) {
          new TC('f').run(tr);
        }


        var tc = new TC('f');
        assertSame(tc.countTestCases(), 1);
        assertSame(tc + '', 'f');
        tc.name = 'test';
        assertSame(repr(tc), '<TestCase f(test)>');

        run(TC);
        assert(tested && setUp && tearedDown);
        tested = setUp = tearedDown = false;

        var BadSetUpTC = TC.subclass({setUp: thrower(1)});
        run(BadSetUpTC);
        assert(!tested && !setUp && !tearedDown);

        var ErrorTC = TC.subclass({f: thrower(2)});
        run(ErrorTC);
        assert(!tested && setUp && tearedDown);
        setUp = tearedDown = false;

        var assertionError = new AssertionError(3);
        var FailureTC = TC.subclass({f: thrower(assertionError)});
        run(FailureTC);
        assert(!tested && setUp && tearedDown);
        setUp = tearedDown = false;

        var BadTearDownTC = TC.subclass({tearDown: thrower(4)});
        run(BadTearDownTC);
        assert(tested && setUp && !tearedDown);

        assertEqual(tr.errors.map(function (error) { return error[1]; }),
                    [1, 2, 4]);
        assertEqual(tr.failures.map(function (failure) { return failure[1]; }),
                    [assertionError]);
        assertSame(started, stopped);
        assertSame(started, 5);
      },

      testTestSuite: function () {
        var TC = TestCase.subclass({f: function () {}, g: function () {}});
        var ts = new TestSuite([new TC('f'), new TC('g')]);
        ts.addTest(new TC('f'));
        assertSame(ts.countTestCases(), 3);
        assertSame(repr(ts), '<TestSuite f, g, f>');
        var tr = new TestResult();
        ts.run(tr);
        assertSame(tr.testsRun, 3);
      },

      testStreamTestResult: function () {
        var stream = new Stream();
        var ttr = new StreamTestResult(stream);
        ttr.startTest('hi');
        ttr.addError();
        ttr.addFailure();
        ttr.addSuccess();
        assertSame(stream.read(), 'hi ERROR\n FAIL\n ok\n');
      },

      testLoadTestSuite: function () {
        var ts = new TestSuite();
        assertSame(loadTestSuite(ts), ts);
        var TC = TestCase.subclass(
          {
            name: 'TC',
            test1: function () {},
            test2: function () {},
            func: function () {},
            test3: 42
          });
        assertSame(repr(loadTestSuite(new TC('test1'))),
                   '<TestSuite test1(TC)>');
        assertSame(repr(loadTestSuite(TC)),
                   '<TestSuite test1(TC), test2(TC)>');
        var m = {
          ts: new TestSuite([new TC('func')]),
          tc: new TC('test1'),
          TC: TC
        };
        assertSame(loadTestSuite(m) + '',
                   'func(TC), test1(TC), test1(TC), test2(TC)');
        assertSame(loadTestSuite([m, new TC('test2')]) + '',
                   'func(TC), test1(TC), test1(TC), test2(TC), test2(TC)');
        assertThrow(TypeError, loadTestSuite, 42);
      },

      testRunTestViaStream: function () {
        var TC = TestCase.subclass(
          {
            name: 'test',
            testOk: function () {},
            testError: thrower(1),
            testAssert: partial(assert, false, 'msg1'),
            testAssertEqual: partial(assertEqual, 1, 2, 'msg2'),
            testAssertThrow: partial(assertThrow, Error, thrower(1))
          });
        var stream = new Stream();
        runTestViaStream(loadTestSuite(TC), stream);
        assert(stream.read().startsWith(
                 'testOk(test) ok\n' +
                 'testError(test) ERROR\n' +
                 'testAssert(test) FAIL\n' +
                 'testAssertEqual(test) FAIL\n' +
                 'testAssertThrow(test) FAIL\n' +
                 '=====\n'),
               'TextTestRunner');
      },

      testTestClient: function () {
        var users = ['user1', 'user2'];
        var apps = {
          app1: {
            admin: 'user1'
          },
          app2: {
            admin: 'user2',
            developers: ['user1']
          },
          app3: {
            admin: 'user1'
          },
          app4: {
            admin: 'user2',
            developers: ['user1']
          }
        };
        var client = new TestClient(users, apps);
        var aspect = weave(
          InsteadOf, require.main.exports, 'main',
          function (request) { return eval(request.data + ''); });
        function request(data) {
          return client.request({data: data});
        }

        client.login('no such user');
        client.login('user1');
        assertSame(client.get(), undefined);
        assertSame(request('request.user'), 'user1');
        assertSame(client.request({user: 'user2', data: 'request.user'}),
                   'user2');
        client.logout();
        assertSame(request('request.user'), '');
        assertSame(request('request.method'), 'get');
        assertSame(request('request.path'), '/');
        assertSame(request('typeof(request.get)'), 'object');
        assertSame(request('typeof(request.post)'), 'object');
        assertSame(request('typeof(request.headers)'), 'object');
        assertSame(request('typeof(request.files)'), 'object');

        assertSame(client.get({data: 'request.method'}), 'get');
        assertSame(client.put({data: 'request.method'}), 'put');
        assertSame(client.post({data: 'request.method'}), 'post');
        assertSame(client.del({data: 'request.method'}), 'delete');

        assertEqual(items(request('getAppDescription("app1")')),
                    [
                      ['admin', 'user1'],
                      ['developers', []],
                      ['summary', ''],
                      ['description', ''],
                      ['labels', []],
                      ['name', 'app1']
                    ]);
        assertEqual(request('getAdminedApps("user1")'), ['app1', 'app3']);
        assertEqual(request('getDevelopedApps("user1")'), ['app2', 'app4']);
        assertEqual(request('getDevelopedApps("user2")'), []);
        assertThrow(
          NoSuchUserError, request, 'getAdminedApps("no such user")');
        assertThrow(
          NoSuchUserError, request, 'getDevelopedApps("no such user")');

        var context = {};
        assertSame(
          request(
            'new Response(new Template("").render(context))').context,
          context);

        var H = Handler.subclass(
          {
            get: function () { return {}; }
          });
        assertSame(request('new H().handle({method: "get"})').handler, H);

        aspect.unweave();
      }
    });

  //////////////////////////////////////////////////////////////////////////////
  // core tests
  //////////////////////////////////////////////////////////////////////////////

  exports.CoreTestCase = TestCase.subclass(
    {
      name: 'core',

      setUp: function () {
        this._clean();
        db.create('X', {n: db.number, b: db.bool, s: db.string});
        db.insert('X', {n: 0, b: false, s: 'zero'});
        db.insert('X', {n: 1, b: false, s: 'one'});
        db.insert('X', {n: 42, b: true, s: 'the answer'});
      },

      tearDown: function () {
        this._clean();
      },

      _clean: function () {
        fs.list('').forEach(fs.remove);
        db.drop(db.list());
      },

      testQuery: function () {
        assert(db.query('X') instanceof Array);
        assertEqual(db.query('X where !b').length, 2);
        assertEqual(db.query('X.n where !b', [], 'n').map(items),
                    [[['n', 0]], [['n', 1]]]);
        assertEqual(
          db.query('X[b, n]', [], ['b', 'n * $'], [-1]).map(items),
          [
            [['b', false], ['n', 1]],
            [['b', false], ['n', 0]],
            [['b', true], ['n', 42]]
          ]);
        assertEqual(
          db.query('X.b where n > $1 && s != $2', [0, 'one']).map(items),
          [[['b', true]]]);
        assertEqual(
          db.query('X.n', [], 'n', [], 1, 1).map(items),
          [[['n', 1]]]);
      },

      testCount: function () {
        assertSame(db.count('X'), 3);
        assertSame(db.count('X.b where n > $1 && s != $2', [0, 'one']), 1);
      },

      testCreate: function () {
        db.create('Y', {});
        assertEqual(items(db.getHeader('Y')), []);
        assertEqual(items(db.getHeader('X')).sort(),
                    [['b', 'bool'], ['n', 'number'], ['s', 'string']]);
        db.drop(['Y']);
        db.create('Y',
                  {
                    s: db.number.serial(),
                    i: db.number.integer(),
                    u: db.string.unique(),
                    f: db.string.foreign('Y', 'u'),
                    c: db.number.check('c % 2 == 0'),
                    s1: db.number.integer(),
                    i1: db.number.integer(),
                    d: db.number.default_('15')
                  },
                  {
                    unique: [['s', 'i']],
                    foreign: [[['s1', 'i1'], 'Y', ['s', 'i']]],
                    check: ['s != i']
                  });
        assertEqual(
          db.getForeign('Y').sort(),
          [[['f'], 'Y', ['u']], [['s1', 'i1'], 'Y', ['s', 'i']]]);
        assertEqual(db.getUnique('Y').sort(), [['s', 'i'], ['u']]);
        assertEqual(db.getSerial('Y'), ['s']);
        assertEqual(db.getInteger('Y').sort(), ['i', 'i1', 's', 's1']);
        assertEqual(items(db.getDefault('Y')), [['d', 15]]);
        assertEqual(db.list(), ['X', 'Y']);
        db.drop(['Y']);
        assertEqual(db.list(), ['X']);
      },

      testDrop: function () {
        db.create('Y', {u: db.number.unique()});
        db.create('Z', {f: db.number.foreign('Y', 'u')});
        assertThrow(RelVarDependencyError, db.drop, ['Y']);
        db.drop(['Y', 'Z']);
      },

      testConstruct: function () {
        function C() {
          this.args = Array.slice(arguments);
        }
        assertEqual(construct(C, [1, 2]).args, [1, 2]);
        assertEqual(construct(C, {0: 1, 1: 2, length: 2}).args, [1, 2]);
      },

      testDescribeApp: function () {
        var description = getAppDescription('ak');
        assertSame(description.name, 'ak');
      },

      testRemove: function () {
        fs.createDir('dir');
        fs.createDir('dir/subdir');
        fs.write('file', 'hello');
        fs.write('dir/subdir/f', 'hi');
        fs.remove('dir');
        fs.remove('file');
        assertEqual(fs.list(''), [], 'fs.remove');
      },

      testRead: function () {
        assertThrow(NoSuchEntryError, fs.read, 'no such entry');
        fs.write('file', 'hello');
        assertSame(fs.read('file') + '', 'hello');
      },

      testRequestApp: function() {
        var oldRequestApp = _core.requestApp;
        _core.requestApp = function (appName, request, files, data) {
          return new Binary(
            [
              data,
              '',
              repr(JSON.parse(request)[appName])
            ].concat(files).join('\n'));
        };

        var response = requestApp('method', {data: '201'});
        assertSame(response.status, 201);
        assertSame(response.content + '', '"get"');
        assertEqual(items(response.headers), []);
        assertSame(requestApp('path', {data: '200'}).content + '', '"/"');
        assertSame(requestApp('path', {data: '200', path: 'a/b'}).content + '',
                   '"/a/b"');
        assertSame(requestApp('post', {data: '200', post: 'hi'}).content + '',
                   '"hi"');
        assertSame(requestApp('get', {data: '200'}).content + '', '{}');
        assertSame(requestApp('fileNames',
{'fraction_non_alphanumeric': 0.19083477493979173, 'fraction_numerical': 0.010176045588684237, 'mean_word_length': 1.8416385542168674, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2571472', 'n_tokens_mistral': 8737, 'n_tokens_neox': 8269, 'n_words': 1949}