mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-10 18:49:02 -06:00
224 lines
6.9 KiB
JavaScript
224 lines
6.9 KiB
JavaScript
// @flow
|
|
|
|
import {suite, test} from 'mocha';
|
|
import MemoryStore from './memory-store.js';
|
|
import {emptyRef} from './ref.js';
|
|
import {assert} from 'chai';
|
|
import {default as Database, getDatasTypes, newCommit} from './database.js';
|
|
import {invariant, notNull} from './assert.js';
|
|
import {newMap} from './map.js';
|
|
import {stringType} from './type.js';
|
|
import {getRef} from './get-ref.js';
|
|
import {encodeNomsValue} from './encode.js';
|
|
|
|
suite('Database', () => {
|
|
test('access', async () => {
|
|
const ms = new MemoryStore();
|
|
const db = new Database(ms);
|
|
const input = 'abc';
|
|
|
|
const c = encodeNomsValue(input, stringType);
|
|
const v1 = await db.readValue(c.ref);
|
|
assert.equal(null, v1);
|
|
|
|
ms.put(c);
|
|
const v2 = await db.readValue(c.ref);
|
|
assert.equal('abc', v2);
|
|
});
|
|
|
|
test('commit', async () => {
|
|
const ms = new MemoryStore();
|
|
let db = new Database(ms);
|
|
const datasetID = 'ds1';
|
|
|
|
const datasets = await db.datasets();
|
|
assert.isTrue(datasets.isEmpty());
|
|
|
|
// |a|
|
|
const aCommit = await newCommit('a');
|
|
const db2 = await db.commit(datasetID, aCommit);
|
|
|
|
// The old database still still has no head.
|
|
assert.isNull(await db.head(datasetID));
|
|
|
|
// The new database has |a|.
|
|
const aRef = notNull(await db2.headRef(datasetID));
|
|
assert.isTrue(aCommit.ref.equals(aRef.targetRef));
|
|
const aCommit1 = notNull(await db2.head(datasetID));
|
|
assert.strictEqual('a', aCommit1.value);
|
|
db = db2;
|
|
|
|
// |a| <- |b|
|
|
const bCommit = await newCommit('b', [aRef]);
|
|
db = await db.commit(datasetID, bCommit);
|
|
const bRef = notNull(await db.headRef(datasetID));
|
|
assert.isTrue(bCommit.ref.equals(bRef.targetRef));
|
|
assert.strictEqual('b', notNull(await db.head(datasetID)).value);
|
|
|
|
// |a| <- |b|
|
|
// \----|c|
|
|
// Should be disallowed.
|
|
const cCommit = await newCommit('c');
|
|
let message = '';
|
|
try {
|
|
await db.commit(datasetID, cCommit);
|
|
throw new Error('not reached');
|
|
} catch (ex) {
|
|
message = ex.message;
|
|
}
|
|
assert.strictEqual('Merge needed', message);
|
|
assert.strictEqual('b', notNull(await db.head(datasetID)).value);
|
|
|
|
// |a| <- |b| <- |d|
|
|
const dCommit = await newCommit('d', [bRef]);
|
|
db = await db.commit(datasetID, dCommit);
|
|
const dRef = notNull(await db.headRef(datasetID));
|
|
assert.isTrue(dCommit.ref.equals(dRef.targetRef));
|
|
assert.strictEqual('d', notNull(await db.head(datasetID)).value);
|
|
|
|
// Attempt to recommit |b| with |a| as parent.
|
|
// Should be disallowed.
|
|
try {
|
|
await db.commit(datasetID, bCommit);
|
|
throw new Error('not reached');
|
|
} catch (ex) {
|
|
message = ex.message;
|
|
}
|
|
// assert.strictEqual('Merge needed', message);
|
|
assert.strictEqual('d', notNull(await db.head(datasetID)).value);
|
|
|
|
// Add a commit to a different datasetId
|
|
db = await db.commit('otherDb', aCommit);
|
|
assert.strictEqual('a', notNull(await db.head('otherDb')).value);
|
|
|
|
// Get a fresh database, and verify that both datasets are present
|
|
const newDb = new Database(ms);
|
|
assert.strictEqual('d', notNull(await newDb.head(datasetID)).value);
|
|
assert.strictEqual('a', notNull(await newDb.head('otherDb')).value);
|
|
});
|
|
|
|
test('concurrency', async () => {
|
|
const ms = new MemoryStore();
|
|
let db = new Database(ms);
|
|
const datasetID = 'ds1';
|
|
|
|
// |a|
|
|
const aCommit = await newCommit('a');
|
|
db = await db.commit(datasetID, aCommit);
|
|
const aRef = notNull(await db.headRef(datasetID));
|
|
const bCommit = await newCommit('b', [aRef]);
|
|
db = await db.commit(datasetID, bCommit);
|
|
const bRef = notNull(await db.headRef(datasetID));
|
|
assert.strictEqual('b', notNull(await db.head(datasetID)).value);
|
|
|
|
// Important to create this here.
|
|
let db2 = new Database(ms);
|
|
|
|
// Change 1:
|
|
// |a| <- |b| <- |c|
|
|
const cCommit = await newCommit('c', [bRef]);
|
|
db = await db.commit(datasetID, cCommit);
|
|
assert.strictEqual('c', notNull(await db.head(datasetID)).value);
|
|
|
|
// Change 2:
|
|
// |a| <- |b| <- |e|
|
|
// Should be disallowed, Database returned by Commit() should have |c| as Head.
|
|
const eCommit = await newCommit('e', [bRef]);
|
|
let message = '';
|
|
try {
|
|
db2 = await db2.commit(datasetID, eCommit);
|
|
throw new Error('not reached');
|
|
} catch (ex) {
|
|
message = ex.message;
|
|
}
|
|
assert.strictEqual('Merge needed', message);
|
|
assert.strictEqual('c', notNull(await db.head(datasetID)).value);
|
|
});
|
|
|
|
|
|
test('empty datasets', async () => {
|
|
const ms = new MemoryStore();
|
|
const db = new Database(ms);
|
|
const datasets = await db.datasets();
|
|
assert.strictEqual(0, datasets.size);
|
|
});
|
|
|
|
test('head', async () => {
|
|
const ms = new MemoryStore();
|
|
let db = new Database(ms);
|
|
const types = getDatasTypes();
|
|
|
|
const commit = await newCommit('foo', []);
|
|
|
|
const commitRef = db.writeValue(commit);
|
|
const datasets = await newMap(['foo', commitRef], types.commitMapType);
|
|
const rootRef = db.writeValue(datasets).targetRef;
|
|
assert.isTrue(await ms.updateRoot(rootRef, emptyRef));
|
|
db = new Database(ms); // refresh the datasets
|
|
|
|
assert.strictEqual(1, datasets.size);
|
|
const fooHead = await db.head('foo');
|
|
invariant(fooHead);
|
|
assert.isTrue(fooHead.equals(commit));
|
|
const barHead = await db.head('bar');
|
|
assert.isNull(barHead);
|
|
});
|
|
|
|
test('writeValue primitives', async () => {
|
|
const db = new Database(new MemoryStore());
|
|
|
|
const r1 = db.writeValue('hello').targetRef;
|
|
const r2 = db.writeValue(false).targetRef;
|
|
const r3 = db.writeValue(2).targetRef;
|
|
|
|
const v1 = await db.readValue(r1);
|
|
assert.equal('hello', v1);
|
|
const v2 = await db.readValue(r2);
|
|
assert.equal(false, v2);
|
|
const v3 = await db.readValue(r3);
|
|
assert.equal(2, v3);
|
|
});
|
|
|
|
test('caching', async () => {
|
|
const ms = new MemoryStore();
|
|
const db = new Database(ms, 1e6);
|
|
|
|
const r1 = db.writeValue('hello').targetRef;
|
|
(ms: any).get = (ms: any).put = () => { assert.fail('unreachable'); };
|
|
const v1 = await db.readValue(r1);
|
|
assert.equal(v1, 'hello');
|
|
const r2 = db.writeValue('hello').targetRef;
|
|
assert.isTrue(r1.equals(r2));
|
|
});
|
|
|
|
test('caching eviction', async () => {
|
|
const ms = new MemoryStore();
|
|
const db = new Database(ms, 15);
|
|
|
|
const r1 = db.writeValue('hello').targetRef;
|
|
const r2 = db.writeValue('world').targetRef;
|
|
(ms: any).get = () => { throw new Error(); };
|
|
const v2 = await db.readValue(r2);
|
|
assert.equal(v2, 'world');
|
|
let ex;
|
|
try {
|
|
await db.readValue(r1);
|
|
} catch (e) {
|
|
ex = e;
|
|
}
|
|
assert.instanceOf(ex, Error);
|
|
});
|
|
|
|
test('caching has', async () => {
|
|
const ms = new MemoryStore();
|
|
const db = new Database(ms, 1e6);
|
|
|
|
const r1 = getRef('hello', stringType);
|
|
const v1 = await db.readValue(r1);
|
|
assert.equal(v1, null);
|
|
(ms: any).get = (ms: any).has = () => { assert.fail('unreachable'); };
|
|
const v2 = await db.readValue(r1);
|
|
assert.equal(v2, null);
|
|
});
|
|
});
|