@creative-introvert/tons-of-tests 3.0.0-rc.0 → 3.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/RepositoryError.js +20 -2
- package/dist/cjs/RepositoryError.js.map +1 -1
- package/dist/cjs/Test.js +11 -8
- package/dist/cjs/Test.js.map +1 -1
- package/dist/cjs/Test.repository.js +23 -11
- package/dist/cjs/Test.repository.js.map +1 -1
- package/dist/cjs/TestRun.js +17 -9
- package/dist/cjs/TestRun.js.map +1 -1
- package/dist/cjs/internal/Test.js +2 -5
- package/dist/cjs/internal/Test.js.map +1 -1
- package/dist/cjs/internal/Test.repository.sqlite.js +400 -174
- package/dist/cjs/internal/Test.repository.sqlite.js.map +1 -1
- package/dist/cjs/internal/stats.js +3 -3
- package/dist/cjs/internal/stats.js.map +1 -1
- package/dist/cjs/internal/summarize.js +1 -1
- package/dist/cjs/internal/summarize.js.map +1 -1
- package/dist/cjs/internal/test-repository-fixtures.js +1 -1
- package/dist/cjs/internal/test-repository-fixtures.js.map +1 -1
- package/dist/cjs/internal/test-repository-schema.js +98 -0
- package/dist/cjs/internal/test-repository-schema.js.map +1 -0
- package/dist/dts/RepositoryError.d.ts +30 -2
- package/dist/dts/RepositoryError.d.ts.map +1 -1
- package/dist/dts/Test.d.ts +29 -12
- package/dist/dts/Test.d.ts.map +1 -1
- package/dist/dts/Test.repository.d.ts +63 -26
- package/dist/dts/Test.repository.d.ts.map +1 -1
- package/dist/dts/TestRun.d.ts +16 -12
- package/dist/dts/TestRun.d.ts.map +1 -1
- package/dist/dts/internal/Test.d.ts +13 -1
- package/dist/dts/internal/Test.d.ts.map +1 -1
- package/dist/dts/internal/Test.repository.sqlite.d.ts +80 -53
- package/dist/dts/internal/Test.repository.sqlite.d.ts.map +1 -1
- package/dist/dts/internal/stats.d.ts +1 -0
- package/dist/dts/internal/stats.d.ts.map +1 -1
- package/dist/dts/internal/test-repository-fixtures.d.ts +13 -2
- package/dist/dts/internal/test-repository-fixtures.d.ts.map +1 -1
- package/dist/dts/internal/test-repository-schema.d.ts +16 -0
- package/dist/dts/internal/test-repository-schema.d.ts.map +1 -0
- package/dist/esm/RepositoryError.js +16 -1
- package/dist/esm/RepositoryError.js.map +1 -1
- package/dist/esm/Test.js +11 -8
- package/dist/esm/Test.js.map +1 -1
- package/dist/esm/Test.repository.js +25 -11
- package/dist/esm/Test.repository.js.map +1 -1
- package/dist/esm/TestRun.js +17 -8
- package/dist/esm/TestRun.js.map +1 -1
- package/dist/esm/internal/Test.js +2 -5
- package/dist/esm/internal/Test.js.map +1 -1
- package/dist/esm/internal/Test.repository.sqlite.js +397 -174
- package/dist/esm/internal/Test.repository.sqlite.js.map +1 -1
- package/dist/esm/internal/stats.js +3 -3
- package/dist/esm/internal/stats.js.map +1 -1
- package/dist/esm/internal/summarize.js +1 -1
- package/dist/esm/internal/summarize.js.map +1 -1
- package/dist/esm/internal/test-repository-fixtures.js +1 -1
- package/dist/esm/internal/test-repository-fixtures.js.map +1 -1
- package/dist/esm/internal/test-repository-schema.js +92 -0
- package/dist/esm/internal/test-repository-schema.js.map +1 -0
- package/package.json +4 -4
- package/dist/cjs/internal/test-repository-migrations.js +0 -68
- package/dist/cjs/internal/test-repository-migrations.js.map +0 -1
- package/dist/dts/internal/test-repository-migrations.d.ts +0 -11
- package/dist/dts/internal/test-repository-migrations.d.ts.map +0 -1
- package/dist/esm/internal/test-repository-migrations.js +0 -62
- package/dist/esm/internal/test-repository-migrations.js.map +0 -1
|
@@ -3,21 +3,26 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.makeTestRepository = exports.TestResultStorage = void 0;
|
|
6
|
+
exports.makeTestRepository = exports.TestResultStorage = exports.TestRepositoryStorage = void 0;
|
|
7
7
|
Object.defineProperty(exports, "tables", {
|
|
8
8
|
enumerable: true,
|
|
9
9
|
get: function () {
|
|
10
|
-
return
|
|
10
|
+
return _testRepositorySchema.tables;
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
|
+
var Sqlite = /*#__PURE__*/_interopRequireWildcard(/*#__PURE__*/require("@effect/sql-sqlite-node"));
|
|
13
14
|
var _effect = /*#__PURE__*/require("effect");
|
|
14
15
|
var _sql = /*#__PURE__*/require("effect/sql");
|
|
15
16
|
var _TestRun = /*#__PURE__*/require("../TestRun.js");
|
|
16
17
|
var _RepositoryError = /*#__PURE__*/require("../RepositoryError.js");
|
|
17
18
|
var _Classify = /*#__PURE__*/require("./Classify.js");
|
|
18
|
-
var
|
|
19
|
+
var _testRepositorySchema = /*#__PURE__*/require("./test-repository-schema.js");
|
|
19
20
|
var _testResultStats = /*#__PURE__*/require("./test-result-stats.js");
|
|
20
21
|
var _TestSerialization = /*#__PURE__*/require("./Test.serialization.js");
|
|
22
|
+
var cache;
|
|
23
|
+
function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var r, c, a = Object.defineProperty, n = { __proto__: null, default: e }; if (Object(e) !== e) return n; if (cache || "function" != typeof WeakMap || (cache = new WeakMap()), cache) { if (cache.has(e)) return cache.get(e); cache.set(e, n); } for (c in e) "default" !== c && {}.hasOwnProperty.call(e, c) && ((r = a && Object.getOwnPropertyDescriptor(e, c)) && (r.get || r.set) ? a(n, c, r) : n[c] = e[c]); return n; }
|
|
24
|
+
class TestRepositoryStorage extends /*#__PURE__*/_effect.Context.Service()('@creative-introvert/tons-of-tests/internal/TestRepositoryStorage') {}
|
|
25
|
+
exports.TestRepositoryStorage = TestRepositoryStorage;
|
|
21
26
|
const TestResultStorage = exports.TestResultStorage = /*#__PURE__*/_effect.Schema.Struct({
|
|
22
27
|
id: _effect.Schema.String,
|
|
23
28
|
hashTestCase: _effect.Schema.String,
|
|
@@ -37,33 +42,187 @@ const StoredResult = /*#__PURE__*/_effect.Schema.Struct({
|
|
|
37
42
|
tags: /*#__PURE__*/_effect.Schema.fromJsonString(/*#__PURE__*/_effect.Schema.Array(_effect.Schema.String)),
|
|
38
43
|
timeMillis: _effect.Schema.Finite
|
|
39
44
|
});
|
|
45
|
+
const RawResult = /*#__PURE__*/StoredResult.mapFields(fields => ({
|
|
46
|
+
...fields,
|
|
47
|
+
tags: _effect.Schema.String
|
|
48
|
+
}));
|
|
49
|
+
const StoredRun = /*#__PURE__*/_effect.Schema.Union([/*#__PURE__*/_TestRun.TestRun.members[0].pipe(/*#__PURE__*/_effect.Schema.fieldsAssign({
|
|
50
|
+
executionId: _effect.Schema.String
|
|
51
|
+
})), /*#__PURE__*/_TestRun.TestRun.members[1].pipe(/*#__PURE__*/_effect.Schema.fieldsAssign({
|
|
52
|
+
executionId: _effect.Schema.String
|
|
53
|
+
}))]);
|
|
40
54
|
const encodeResult = /*#__PURE__*/_effect.Schema.encodeEffect(StoredResult);
|
|
41
55
|
const decodeRows = /*#__PURE__*/_effect.Schema.decodeUnknownEffect(/*#__PURE__*/_effect.Schema.Array(StoredResult));
|
|
56
|
+
const decodeRawRows = /*#__PURE__*/_effect.Schema.decodeUnknownEffect(/*#__PURE__*/_effect.Schema.Array(RawResult));
|
|
42
57
|
const decodeRuns = /*#__PURE__*/_effect.Schema.decodeUnknownEffect(/*#__PURE__*/_effect.Schema.Array(_TestRun.TestRun));
|
|
43
|
-
const
|
|
44
|
-
|
|
58
|
+
const decodeStoredRuns = /*#__PURE__*/_effect.Schema.decodeUnknownEffect(/*#__PURE__*/_effect.Schema.Array(StoredRun));
|
|
59
|
+
const decodeListedRuns = /*#__PURE__*/_effect.Schema.decodeUnknownEffect(/*#__PURE__*/_effect.Schema.Array(/*#__PURE__*/_TestRun.TestRun.members[1].pipe(/*#__PURE__*/_effect.Schema.fieldsAssign({
|
|
45
60
|
isBaseline: /*#__PURE__*/_effect.Schema.Literals([0, 1])
|
|
46
|
-
})));
|
|
61
|
+
}))));
|
|
47
62
|
const decodeVariantBaselines = /*#__PURE__*/_effect.Schema.decodeUnknownEffect(/*#__PURE__*/_effect.Schema.Array(/*#__PURE__*/_effect.Schema.Struct({
|
|
48
63
|
variant: _effect.Schema.String,
|
|
49
|
-
id: /*#__PURE__*/_effect.Schema.NullOr(
|
|
50
|
-
name: /*#__PURE__*/_effect.Schema.NullOr(
|
|
51
|
-
state: /*#__PURE__*/_effect.Schema.NullOr(
|
|
64
|
+
id: /*#__PURE__*/_effect.Schema.NullOr(_effect.Schema.Int),
|
|
65
|
+
name: /*#__PURE__*/_effect.Schema.NullOr(_effect.Schema.String),
|
|
66
|
+
state: /*#__PURE__*/_effect.Schema.NullOr(/*#__PURE__*/_effect.Schema.Literal('Complete'))
|
|
52
67
|
})));
|
|
53
68
|
const decodeRunLimit = /*#__PURE__*/_effect.Schema.decodeUnknownEffect(/*#__PURE__*/_effect.Schema.Int.check(/*#__PURE__*/_effect.Schema.isBetween({
|
|
54
69
|
minimum: 1,
|
|
55
70
|
maximum: 100
|
|
56
71
|
})));
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
yield*
|
|
61
|
-
const
|
|
62
|
-
|
|
72
|
+
const openClient = options => _effect.Layer.build(Sqlite.SqliteClient.layer(options)).pipe(_effect.Effect.map(_effect.Context.get(_sql.SqlClient.SqlClient)));
|
|
73
|
+
const makeTestRepository = exports.makeTestRepository = /*#__PURE__*/_effect.Effect.fnUntraced(function* (options) {
|
|
74
|
+
const fs = yield* _effect.FileSystem.FileSystem;
|
|
75
|
+
const path = yield* _effect.Path.Path;
|
|
76
|
+
const crypto = yield* _effect.Crypto.Crypto;
|
|
77
|
+
const lifetime = yield* _effect.Effect.scope;
|
|
78
|
+
const memoryLock = yield* _effect.TxReentrantLock.make();
|
|
79
|
+
const snapshotLock = yield* _effect.Semaphore.make(1);
|
|
80
|
+
const snapshotMemory = yield* _effect.Ref.make(_effect.Option.none());
|
|
81
|
+
const canonicalPath = filename => fs.realPath(filename).pipe(_effect.Effect.catchTag('PlatformError', error => {
|
|
82
|
+
if (error.reason._tag !== 'NotFound') return _effect.Effect.fail(error);
|
|
83
|
+
const parent = path.dirname(filename);
|
|
84
|
+
if (parent === filename) return _effect.Effect.fail(error);
|
|
85
|
+
return _effect.Effect.gen(function* () {
|
|
86
|
+
const link = yield* fs.readLink(filename).pipe(_effect.Effect.asSome, _effect.Effect.catchReason('PlatformError', 'NotFound', () => _effect.Effect.succeedNone));
|
|
87
|
+
return _effect.Option.isSome(link) ? yield* canonicalPath(path.resolve(parent, link.value)) : path.join(yield* canonicalPath(parent), path.basename(filename));
|
|
88
|
+
});
|
|
89
|
+
}));
|
|
90
|
+
if (options.dbPath !== ':memory:' && options.cachePath !== ':memory:') {
|
|
91
|
+
const [snapshotFile, cacheFile] = yield* _effect.Effect.all([canonicalPath(options.dbPath), canonicalPath(options.cachePath)]);
|
|
92
|
+
const [snapshotInfo, cacheInfo] = yield* _effect.Effect.forEach([snapshotFile, cacheFile], filename => fs.stat(filename).pipe(_effect.Effect.asSome, _effect.Effect.catchReason('PlatformError', 'NotFound', () => _effect.Effect.succeedNone)));
|
|
93
|
+
const sameInode = _effect.Option.isSome(snapshotInfo) && _effect.Option.isSome(cacheInfo) && snapshotInfo.value.dev === cacheInfo.value.dev && _effect.Option.isSome(snapshotInfo.value.ino) && _effect.Option.isSome(cacheInfo.value.ino) && snapshotInfo.value.ino.value === cacheInfo.value.ino.value;
|
|
94
|
+
if (snapshotFile === cacheFile || sameInode) {
|
|
95
|
+
return yield* new _RepositoryError.RepositoryPathConflict({
|
|
96
|
+
snapshotPath: options.dbPath,
|
|
97
|
+
cachePath: options.cachePath
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
let initializeCache = options.cachePath === ':memory:';
|
|
102
|
+
if (options.cachePath !== ':memory:') {
|
|
103
|
+
yield* fs.makeDirectory(path.dirname(options.cachePath), {
|
|
104
|
+
recursive: true
|
|
105
|
+
});
|
|
106
|
+
initializeCache = !(yield* fs.exists(options.cachePath));
|
|
107
|
+
if (!initializeCache) {
|
|
108
|
+
const compatible = yield* _effect.Effect.scoped(_effect.Effect.gen(function* () {
|
|
109
|
+
const sql = yield* openClient({
|
|
110
|
+
filename: options.cachePath,
|
|
111
|
+
readonly: true,
|
|
112
|
+
disableWAL: true
|
|
113
|
+
});
|
|
114
|
+
yield* (0, _testRepositorySchema.checkFormat)(sql, 'cache', options.cachePath);
|
|
115
|
+
return true;
|
|
116
|
+
})).pipe(_effect.Effect.catchTag('RepositoryFormatError', () => _effect.Effect.succeed(false)));
|
|
117
|
+
if (!compatible) {
|
|
118
|
+
for (const suffix of ['', '-wal', '-shm']) {
|
|
119
|
+
yield* fs.remove(`${options.cachePath}${suffix}`, {
|
|
120
|
+
force: true
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
initializeCache = true;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const cache = yield* openClient({
|
|
128
|
+
filename: options.cachePath
|
|
129
|
+
});
|
|
130
|
+
yield* cache`PRAGMA foreign_keys = ON;`;
|
|
131
|
+
if (initializeCache) yield* (0, _testRepositorySchema.initializeSchema)(cache, 'cache', options.cachePath);
|
|
132
|
+
const getMemorySnapshot = snapshotLock.withPermit(_effect.Effect.gen(function* () {
|
|
133
|
+
const existing = yield* _effect.Ref.get(snapshotMemory);
|
|
134
|
+
if (_effect.Option.isSome(existing)) return existing.value;
|
|
135
|
+
const sql = yield* _effect.Scope.provide(openClient({
|
|
136
|
+
filename: ':memory:',
|
|
137
|
+
disableWAL: true
|
|
138
|
+
}), lifetime);
|
|
139
|
+
yield* sql`PRAGMA foreign_keys = ON;`;
|
|
140
|
+
yield* (0, _testRepositorySchema.initializeSchema)(sql, 'snapshot', ':memory:');
|
|
141
|
+
yield* _effect.Ref.set(snapshotMemory, _effect.Option.some(sql));
|
|
142
|
+
return sql;
|
|
143
|
+
}));
|
|
144
|
+
const snapshotReader = _effect.Effect.gen(function* () {
|
|
145
|
+
if (options.dbPath === ':memory:') return yield* _effect.Ref.get(snapshotMemory);
|
|
146
|
+
if (!(yield* fs.exists(options.dbPath))) return _effect.Option.none();
|
|
147
|
+
const sql = yield* openClient({
|
|
148
|
+
filename: options.dbPath,
|
|
149
|
+
readonly: true,
|
|
150
|
+
disableWAL: true
|
|
151
|
+
});
|
|
152
|
+
yield* (0, _testRepositorySchema.checkFormat)(sql, 'snapshot', options.dbPath);
|
|
153
|
+
return _effect.Option.some(sql);
|
|
154
|
+
});
|
|
155
|
+
const withSnapshot = (empty, use) => _effect.Effect.scoped(_effect.Effect.gen(function* () {
|
|
156
|
+
const sql = yield* snapshotReader;
|
|
157
|
+
return _effect.Option.isSome(sql) ? yield* use(sql.value) : empty;
|
|
158
|
+
}));
|
|
159
|
+
const cacheReader = _effect.Effect.gen(function* () {
|
|
160
|
+
if (options.cachePath === ':memory:') {
|
|
161
|
+
// A memory store has one connection. Block writes to attempts while
|
|
162
|
+
// readers page through them; temporary statistics remain writable.
|
|
163
|
+
yield* _effect.TxReentrantLock.readLock(memoryLock);
|
|
164
|
+
return cache;
|
|
165
|
+
}
|
|
166
|
+
const sql = yield* openClient({
|
|
167
|
+
filename: options.cachePath,
|
|
168
|
+
readonly: true,
|
|
169
|
+
disableWAL: true
|
|
170
|
+
});
|
|
171
|
+
// Keep the selected rows stable until the scoped connection closes,
|
|
172
|
+
// including when reading is interrupted.
|
|
173
|
+
yield* sql`BEGIN;`;
|
|
174
|
+
return sql;
|
|
175
|
+
});
|
|
176
|
+
const writeCache = effect => options.cachePath === ':memory:' ? _effect.TxReentrantLock.withWriteLock(memoryLock, cache.withTransaction(effect)) : cache.withTransaction(effect);
|
|
177
|
+
const readRun = _effect.Effect.fnUntraced(function* (sql, run) {
|
|
178
|
+
return _effect.Array.head(yield* decodeRuns(yield* sql`
|
|
179
|
+
SELECT *, ${run.store} AS store FROM ${sql(_testRepositorySchema.tables.testRuns)}
|
|
180
|
+
WHERE id = ${run.id}
|
|
181
|
+
AND ${run.store === 'cache' ? sql`snapshotIdentity = ${options.snapshotIdentity}` : sql`1 = 1`};
|
|
182
|
+
`));
|
|
183
|
+
});
|
|
184
|
+
const getTestRun = run => run.store === 'cache' ? readRun(cache, run) : withSnapshot(_effect.Option.none(), sql => readRun(sql, run));
|
|
185
|
+
const requireRun = _effect.Effect.fnUntraced(function* (sql, run) {
|
|
186
|
+
const found = yield* readRun(sql, run);
|
|
187
|
+
if (_effect.Option.isNone(found) || found.value.name !== run.name || found.value.variant !== run.variant) {
|
|
188
|
+
return yield* new _RepositoryError.TestRunNotFound({
|
|
189
|
+
testRunId: run.id
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return found.value;
|
|
193
|
+
});
|
|
194
|
+
const writeRows = _effect.Effect.fnUntraced(function* (sql, runId, rows) {
|
|
195
|
+
if (rows.length === 0) return;
|
|
196
|
+
yield* sql`
|
|
197
|
+
INSERT INTO ${sql(_testRepositorySchema.tables.testResults)} ${sql.insert(rows.map(row => ({
|
|
198
|
+
id: row.id,
|
|
199
|
+
hashTestCase: row.hashTestCase,
|
|
200
|
+
key: row.key,
|
|
201
|
+
input: row.input,
|
|
202
|
+
result: row.result,
|
|
203
|
+
expected: row.expected,
|
|
204
|
+
label: row.label
|
|
205
|
+
})))} ON CONFLICT(id) DO NOTHING;
|
|
206
|
+
`;
|
|
207
|
+
yield* sql`
|
|
208
|
+
INSERT INTO ${sql(_testRepositorySchema.tables.testRunResults)} ${sql.insert(rows.map(row => ({
|
|
209
|
+
testRun: runId,
|
|
210
|
+
testResult: row.id,
|
|
211
|
+
ordering: row.ordering,
|
|
212
|
+
resultOrdering: row.resultOrdering,
|
|
213
|
+
timeMillis: row.timeMillis,
|
|
214
|
+
tags: row.tags
|
|
215
|
+
})))} ON CONFLICT(testRun, testResult) DO UPDATE SET
|
|
216
|
+
ordering = excluded.ordering, resultOrdering = excluded.resultOrdering,
|
|
217
|
+
timeMillis = excluded.timeMillis, tags = excluded.tags;
|
|
218
|
+
`;
|
|
63
219
|
});
|
|
64
220
|
const insertTestResults = _effect.Effect.fnUntraced(function* (results, run, schemas) {
|
|
221
|
+
if (run.store !== 'cache') {
|
|
222
|
+
return yield* _effect.Effect.die(new Error('Only cached Running attempts accept result writes.'));
|
|
223
|
+
}
|
|
65
224
|
const codec = (0, _TestSerialization.makeResultCodec)(schemas, run.name);
|
|
66
|
-
//
|
|
225
|
+
// Fifty rows keep every statement below SQLite's 999-parameter minimum.
|
|
67
226
|
for (let offset = 0; offset < Math.max(1, results.length); offset += 50) {
|
|
68
227
|
const encoded = yield* _effect.Effect.forEach(results.slice(offset, offset + 50), _effect.Effect.fnUntraced(function* (result) {
|
|
69
228
|
const encoded = yield* codec.encode(result);
|
|
@@ -74,127 +233,104 @@ const makeTestRepository = exports.makeTestRepository = /*#__PURE__*/_effect.Eff
|
|
|
74
233
|
expected: (0, _TestSerialization.stringifyJson)(encoded.expected)
|
|
75
234
|
});
|
|
76
235
|
}));
|
|
77
|
-
yield*
|
|
78
|
-
const saved = yield*
|
|
79
|
-
if (
|
|
80
|
-
return yield* new _RepositoryError.TestRunNotFound({
|
|
81
|
-
testRunId: run.id
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
if (saved.value.state !== 'Running') {
|
|
236
|
+
yield* writeCache(_effect.Effect.gen(function* () {
|
|
237
|
+
const saved = yield* requireRun(cache, run);
|
|
238
|
+
if (saved.state !== 'Running') {
|
|
85
239
|
return yield* _effect.Effect.die(new Error('Only Running test runs accept result writes.'));
|
|
86
240
|
}
|
|
87
|
-
|
|
88
|
-
const content = encoded.map(result => ({
|
|
89
|
-
id: result.id,
|
|
90
|
-
hashTestCase: result.hashTestCase,
|
|
91
|
-
key: result.key,
|
|
92
|
-
input: result.input,
|
|
93
|
-
result: result.result,
|
|
94
|
-
expected: result.expected,
|
|
95
|
-
label: result.label
|
|
96
|
-
}));
|
|
97
|
-
yield* sql`
|
|
98
|
-
INSERT INTO ${sql(_testRepositoryMigrations.tables.testResults)} ${sql.insert(content)}
|
|
99
|
-
ON CONFLICT(id) DO NOTHING;
|
|
100
|
-
`;
|
|
101
|
-
const links = encoded.map(result => ({
|
|
102
|
-
testRun: run.id,
|
|
103
|
-
testResult: result.id,
|
|
104
|
-
ordering: result.ordering,
|
|
105
|
-
resultOrdering: result.resultOrdering,
|
|
106
|
-
timeMillis: result.timeMillis,
|
|
107
|
-
tags: result.tags
|
|
108
|
-
}));
|
|
109
|
-
yield* sql`
|
|
110
|
-
INSERT INTO ${sql(_testRepositoryMigrations.tables.testRunResults)} ${sql.insert(links)}
|
|
111
|
-
ON CONFLICT(testRun, testResult) DO UPDATE SET
|
|
112
|
-
ordering = excluded.ordering,
|
|
113
|
-
resultOrdering = excluded.resultOrdering,
|
|
114
|
-
timeMillis = excluded.timeMillis,
|
|
115
|
-
tags = excluded.tags;
|
|
116
|
-
`;
|
|
241
|
+
yield* writeRows(cache, run.id, encoded);
|
|
117
242
|
}));
|
|
118
243
|
}
|
|
119
244
|
});
|
|
245
|
+
const readResultPage = (sql, runId, after, matching, limit = 100) => {
|
|
246
|
+
const filter = matching === undefined ? sql`1 = 1` : matching.length === 0 ? sql`1 = 0` : sql.or(matching.map(key => sql`res.hashTestCase = ${key.hashTestCase} AND res.key = ${key.key}`));
|
|
247
|
+
// CROSS JOIN makes matching batches use the case/key index first.
|
|
248
|
+
const from = matching === undefined ? sql`${sql(_testRepositorySchema.tables.testRunResults)} runres JOIN ${sql(_testRepositorySchema.tables.testResults)} res ON res.id = runres.testResult` : sql`${sql(_testRepositorySchema.tables.testResults)} res CROSS JOIN ${sql(_testRepositorySchema.tables.testRunResults)} runres ON res.id = runres.testResult`;
|
|
249
|
+
return sql`
|
|
250
|
+
SELECT res.id, res.hashTestCase, res.key, runres.ordering, runres.resultOrdering,
|
|
251
|
+
res.input, res.result, res.expected, res.label, runres.tags, runres.timeMillis
|
|
252
|
+
FROM ${from}
|
|
253
|
+
WHERE runres.testRun = ${runId} AND ${filter}
|
|
254
|
+
AND ${after === undefined ? sql`1 = 1` : sql`
|
|
255
|
+
(runres.ordering, runres.resultOrdering, runres.testResult) >
|
|
256
|
+
(${after.ordering}, ${after.resultOrdering}, ${after.id})`}
|
|
257
|
+
ORDER BY runres.ordering, runres.resultOrdering, runres.testResult LIMIT ${limit};
|
|
258
|
+
`;
|
|
259
|
+
};
|
|
120
260
|
const getTestResultsStream = (run, schemas, matching) => {
|
|
121
|
-
if (matching !== undefined && matching.length > 50)
|
|
122
|
-
return _effect.Stream.die(new Error('Match at most 50 case/key pairs per query.'));
|
|
123
|
-
}
|
|
261
|
+
if (matching !== undefined && matching.length > 50) return _effect.Stream.die(new Error('Match at most 50 case/key pairs per query.'));
|
|
124
262
|
const codec = (0, _TestSerialization.makeResultCodec)(schemas, run.name);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
FROM ${from}
|
|
138
|
-
WHERE runres.testRun = ${run.id} AND ${filter}
|
|
139
|
-
AND ${after === undefined ? sql`1 = 1` : sql`
|
|
140
|
-
(runres.ordering, runres.resultOrdering, runres.testResult) >
|
|
141
|
-
(${after.ordering}, ${after.resultOrdering}, ${after.id})
|
|
142
|
-
`}
|
|
143
|
-
ORDER BY runres.ordering, runres.resultOrdering, runres.testResult
|
|
144
|
-
LIMIT 100;
|
|
145
|
-
`);
|
|
146
|
-
const last = rows[rows.length - 1];
|
|
147
|
-
const decoded = yield* _effect.Effect.forEach(rows, codec.decode);
|
|
148
|
-
return [decoded, rows.length < 100 ? _effect.Option.none() : _effect.Option.some({
|
|
149
|
-
ordering: last.ordering,
|
|
150
|
-
resultOrdering: last.resultOrdering,
|
|
151
|
-
id: last.id
|
|
152
|
-
})];
|
|
263
|
+
return _effect.Stream.unwrap(_effect.Effect.gen(function* () {
|
|
264
|
+
const selected = run.store === 'cache' ? _effect.Option.some(yield* cacheReader) : yield* snapshotReader;
|
|
265
|
+
if (_effect.Option.isNone(selected)) return yield* new _RepositoryError.TestRunNotFound({
|
|
266
|
+
testRunId: run.id
|
|
267
|
+
});
|
|
268
|
+
const sql = selected.value;
|
|
269
|
+
yield* requireRun(sql, run);
|
|
270
|
+
return _effect.Stream.paginate(undefined, _effect.Effect.fnUntraced(function* (after) {
|
|
271
|
+
const rows = yield* decodeRows(yield* readResultPage(sql, run.id, after, matching));
|
|
272
|
+
const decoded = yield* _effect.Effect.forEach(rows, codec.decode);
|
|
273
|
+
return [decoded, rows.length < 100 ? _effect.Option.none() : _effect.Option.some(rows[rows.length - 1])];
|
|
274
|
+
}));
|
|
153
275
|
}));
|
|
154
276
|
};
|
|
155
277
|
const getLatestTestRun = _effect.Effect.fnUntraced(function* ({
|
|
156
278
|
name,
|
|
157
279
|
variant
|
|
158
280
|
}) {
|
|
159
|
-
return _effect.Array.head(yield* decodeRuns(yield*
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
281
|
+
return _effect.Array.head(yield* decodeRuns(yield* cache`
|
|
282
|
+
SELECT *, 'cache' AS store FROM ${cache(_testRepositorySchema.tables.testRuns)}
|
|
283
|
+
WHERE snapshotIdentity = ${options.snapshotIdentity} AND name = ${name} AND variant = ${variant}
|
|
284
|
+
ORDER BY id DESC LIMIT 1;
|
|
285
|
+
`));
|
|
164
286
|
});
|
|
165
|
-
const getBaselineTestRun =
|
|
287
|
+
const getBaselineTestRun = ({
|
|
166
288
|
name,
|
|
167
289
|
variant
|
|
168
|
-
}) {
|
|
290
|
+
}) => withSnapshot(_effect.Option.none(), _effect.Effect.fnUntraced(function* (sql) {
|
|
169
291
|
return _effect.Array.head(yield* decodeRuns(yield* sql`
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
});
|
|
292
|
+
SELECT run.*, 'snapshot' AS store FROM ${sql(_testRepositorySchema.tables.testBaselines)} baseline
|
|
293
|
+
JOIN ${sql(_testRepositorySchema.tables.testRuns)} run ON run.id = baseline.testRun
|
|
294
|
+
WHERE baseline.name = ${name} AND baseline.variant = ${variant};
|
|
295
|
+
`));
|
|
296
|
+
}));
|
|
175
297
|
const createTestRun = _effect.Effect.fnUntraced(function* (input) {
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
298
|
+
const executionId = yield* crypto.randomUUIDv4();
|
|
299
|
+
return yield* writeCache(_effect.Effect.gen(function* () {
|
|
300
|
+
yield* cache`
|
|
301
|
+
DELETE FROM ${cache(_testRepositorySchema.tables.testRuns)}
|
|
302
|
+
WHERE snapshotIdentity = ${options.snapshotIdentity} AND name = ${input.name} AND variant = ${input.variant};
|
|
303
|
+
`;
|
|
304
|
+
yield* cache`
|
|
305
|
+
DELETE FROM ${cache(_testRepositorySchema.tables.testResults)} WHERE NOT EXISTS (
|
|
306
|
+
SELECT 1 FROM ${cache(_testRepositorySchema.tables.testRunResults)} links WHERE links.testResult = ${cache(_testRepositorySchema.tables.testResults)}.id
|
|
307
|
+
);
|
|
308
|
+
`;
|
|
309
|
+
const inserted = yield* decodeRuns(yield* cache`
|
|
310
|
+
INSERT INTO ${cache(_testRepositorySchema.tables.testRuns)} ${cache.insert({
|
|
311
|
+
name: input.name,
|
|
312
|
+
variant: input.variant,
|
|
313
|
+
executionId,
|
|
314
|
+
snapshotIdentity: options.snapshotIdentity,
|
|
315
|
+
state: 'Running'
|
|
316
|
+
})}
|
|
317
|
+
RETURNING *, 'cache' AS store;
|
|
182
318
|
`);
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
}
|
|
319
|
+
return yield* _effect.Effect.fromOption(_effect.Array.head(inserted)).pipe(_effect.Effect.mapError(() => new _RepositoryError.TestRunNotCreated({
|
|
320
|
+
testSuiteName: input.name
|
|
321
|
+
})));
|
|
322
|
+
}));
|
|
323
|
+
});
|
|
324
|
+
const finishTestRun = (run, state) => writeCache(_effect.Effect.gen(function* () {
|
|
325
|
+
const updated = yield* decodeRuns(yield* cache`
|
|
326
|
+
UPDATE ${cache(_testRepositorySchema.tables.testRuns)} SET state = ${state}
|
|
327
|
+
WHERE id = ${run.id} AND snapshotIdentity = ${options.snapshotIdentity} AND state = 'Running'
|
|
328
|
+
RETURNING *, 'cache' AS store;
|
|
329
|
+
`);
|
|
330
|
+
if (updated.length === 0) return yield* new _RepositoryError.TestRunNotFound({
|
|
331
|
+
testRunId: run.id
|
|
332
|
+
});
|
|
333
|
+
}));
|
|
198
334
|
const withTestRun = (input, execute) => _effect.Effect.acquireUseRelease(createTestRun(input), run => _effect.Effect.suspend(() => execute(run)).pipe(_effect.Effect.map(value => ({
|
|
199
335
|
run,
|
|
200
336
|
value
|
|
@@ -202,39 +338,127 @@ const makeTestRepository = exports.makeTestRepository = /*#__PURE__*/_effect.Eff
|
|
|
202
338
|
run,
|
|
203
339
|
value
|
|
204
340
|
}) => ({
|
|
205
|
-
run:
|
|
206
|
-
|
|
207
|
-
name: run.name,
|
|
208
|
-
variant: run.variant,
|
|
341
|
+
run: _TestRun.TestRun.make({
|
|
342
|
+
...run,
|
|
209
343
|
state: 'Complete'
|
|
210
344
|
}),
|
|
211
345
|
value
|
|
212
346
|
})));
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
347
|
+
const snapshotWriter = _effect.Effect.gen(function* () {
|
|
348
|
+
if (options.dbPath === ':memory:') return yield* getMemorySnapshot;
|
|
349
|
+
const exists = yield* fs.exists(options.dbPath);
|
|
350
|
+
if (exists) {
|
|
351
|
+
yield* _effect.Effect.scoped(_effect.Effect.gen(function* () {
|
|
352
|
+
const sql = yield* openClient({
|
|
353
|
+
filename: options.dbPath,
|
|
354
|
+
readonly: true,
|
|
355
|
+
disableWAL: true
|
|
356
|
+
});
|
|
357
|
+
yield* (0, _testRepositorySchema.checkFormat)(sql, 'snapshot', options.dbPath);
|
|
358
|
+
}));
|
|
359
|
+
} else {
|
|
360
|
+
yield* fs.makeDirectory(path.dirname(options.dbPath), {
|
|
361
|
+
recursive: true
|
|
218
362
|
});
|
|
219
363
|
}
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
|
|
364
|
+
const sql = yield* openClient({
|
|
365
|
+
filename: options.dbPath,
|
|
366
|
+
disableWAL: true
|
|
367
|
+
});
|
|
368
|
+
if (exists) yield* (0, _testRepositorySchema.checkFormat)(sql, 'snapshot', options.dbPath);else yield* (0, _testRepositorySchema.initializeSchema)(sql, 'snapshot', options.dbPath);
|
|
369
|
+
yield* sql`PRAGMA journal_mode = DELETE;`;
|
|
370
|
+
yield* sql`PRAGMA foreign_keys = ON;`;
|
|
371
|
+
return sql;
|
|
372
|
+
});
|
|
373
|
+
const commitTestRuns = runs => _effect.Effect.scoped(_effect.Effect.gen(function* () {
|
|
374
|
+
if (runs.length === 0) return [];
|
|
375
|
+
const selectedVariants = new Set();
|
|
376
|
+
for (const run of runs) {
|
|
377
|
+
const key = JSON.stringify([run.name, run.variant]);
|
|
378
|
+
if (selectedVariants.has(key)) return yield* new _RepositoryError.TestRunDuplicate({
|
|
223
379
|
testSuiteName: run.name,
|
|
224
|
-
testRunId: run.id,
|
|
225
380
|
variant: run.variant
|
|
226
381
|
});
|
|
382
|
+
selectedVariants.add(key);
|
|
227
383
|
}
|
|
228
|
-
yield*
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
384
|
+
const reader = runs.some(run => run.store === 'cache') ? yield* cacheReader : undefined;
|
|
385
|
+
// Check every selected run before opening a writer, so a missing or
|
|
386
|
+
// incomplete attempt cannot create a snapshot file.
|
|
387
|
+
const selected = yield* _effect.Effect.forEach(runs, run => _effect.Effect.gen(function* () {
|
|
388
|
+
const saved = run.store === 'cache' && reader !== undefined ? yield* requireRun(reader, run) : yield* getTestRun(run).pipe(_effect.Effect.flatMap(_effect.Option.match({
|
|
389
|
+
onNone: () => _effect.Effect.fail(new _RepositoryError.TestRunNotFound({
|
|
390
|
+
testRunId: run.id
|
|
391
|
+
})),
|
|
392
|
+
onSome: _effect.Effect.succeed
|
|
393
|
+
})));
|
|
394
|
+
if (saved.name !== run.name || saved.variant !== run.variant) return yield* new _RepositoryError.TestRunNotFound({
|
|
395
|
+
testRunId: run.id
|
|
396
|
+
});
|
|
397
|
+
if (saved.state !== 'Complete') return yield* new _RepositoryError.TestRunIncomplete({
|
|
398
|
+
testSuiteName: saved.name,
|
|
399
|
+
testRunId: saved.id,
|
|
400
|
+
variant: saved.variant
|
|
401
|
+
});
|
|
402
|
+
if (saved.store === 'snapshot' || reader === undefined) return {
|
|
403
|
+
run: saved,
|
|
404
|
+
executionId: undefined
|
|
405
|
+
};
|
|
406
|
+
const stored = yield* decodeStoredRuns(yield* reader`
|
|
407
|
+
SELECT *, 'cache' AS store FROM ${reader(_testRepositorySchema.tables.testRuns)}
|
|
408
|
+
WHERE id = ${saved.id} AND snapshotIdentity = ${options.snapshotIdentity};
|
|
409
|
+
`);
|
|
410
|
+
return {
|
|
411
|
+
run: saved,
|
|
412
|
+
executionId: stored[0].executionId
|
|
413
|
+
};
|
|
414
|
+
}));
|
|
415
|
+
const sql = yield* snapshotWriter;
|
|
416
|
+
return yield* sql.withTransaction(_effect.Effect.forEach(selected, ({
|
|
417
|
+
run,
|
|
418
|
+
executionId
|
|
419
|
+
}) => _effect.Effect.gen(function* () {
|
|
420
|
+
let snapshot;
|
|
421
|
+
if (run.store === 'snapshot') {
|
|
422
|
+
snapshot = yield* requireRun(sql, run);
|
|
423
|
+
} else {
|
|
424
|
+
const existing = yield* decodeRuns(yield* sql`
|
|
425
|
+
SELECT *, 'snapshot' AS store FROM ${sql(_testRepositorySchema.tables.testRuns)} WHERE executionId = ${executionId};
|
|
426
|
+
`);
|
|
427
|
+
if (existing.length > 0) {
|
|
428
|
+
snapshot = existing[0];
|
|
429
|
+
} else {
|
|
430
|
+
const inserted = yield* decodeRuns(yield* sql`
|
|
431
|
+
INSERT INTO ${sql(_testRepositorySchema.tables.testRuns)} ${sql.insert({
|
|
432
|
+
name: run.name,
|
|
433
|
+
variant: run.variant,
|
|
434
|
+
executionId,
|
|
435
|
+
snapshotIdentity: options.snapshotIdentity,
|
|
436
|
+
state: 'Complete'
|
|
437
|
+
})} RETURNING *, 'snapshot' AS store;
|
|
438
|
+
`);
|
|
439
|
+
snapshot = inserted[0];
|
|
440
|
+
let after;
|
|
441
|
+
if (reader !== undefined) {
|
|
442
|
+
while (true) {
|
|
443
|
+
const rows = yield* decodeRawRows(yield* readResultPage(reader, run.id, after, undefined, 50));
|
|
444
|
+
yield* writeRows(sql, snapshot.id, rows);
|
|
445
|
+
if (rows.length < 50) break;
|
|
446
|
+
after = rows[rows.length - 1];
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
yield* sql`
|
|
452
|
+
INSERT INTO ${sql(_testRepositorySchema.tables.testBaselines)} ${sql.insert({
|
|
453
|
+
name: snapshot.name,
|
|
454
|
+
variant: snapshot.variant,
|
|
455
|
+
testRun: snapshot.id
|
|
456
|
+
})}
|
|
457
|
+
ON CONFLICT(name, variant) DO UPDATE SET testRun = excluded.testRun;
|
|
458
|
+
`;
|
|
459
|
+
return snapshot;
|
|
460
|
+
})));
|
|
461
|
+
}));
|
|
238
462
|
const listTestRuns = _effect.Effect.fnUntraced(function* ({
|
|
239
463
|
name,
|
|
240
464
|
variant,
|
|
@@ -242,54 +466,51 @@ const makeTestRepository = exports.makeTestRepository = /*#__PURE__*/_effect.Eff
|
|
|
242
466
|
limit
|
|
243
467
|
}) {
|
|
244
468
|
const parsedLimit = yield* decodeRunLimit(limit);
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
) AS isBaseline
|
|
251
|
-
FROM ${sql(_testRepositoryMigrations.tables.testRuns)} run
|
|
469
|
+
return yield* withSnapshot([], _effect.Effect.fnUntraced(function* (sql) {
|
|
470
|
+
const rows = yield* decodeListedRuns(yield* sql`
|
|
471
|
+
SELECT run.*, 'snapshot' AS store, EXISTS (
|
|
472
|
+
SELECT 1 FROM ${sql(_testRepositorySchema.tables.testBaselines)} baseline
|
|
473
|
+
WHERE baseline.name = run.name AND baseline.variant = run.variant AND baseline.testRun = run.id
|
|
474
|
+
) AS isBaseline FROM ${sql(_testRepositorySchema.tables.testRuns)} run
|
|
252
475
|
WHERE run.name = ${name}
|
|
253
476
|
AND ${variant === undefined ? sql`1 = 1` : sql`run.variant = ${variant}`}
|
|
254
477
|
AND ${before === undefined ? sql`1 = 1` : sql`run.id < ${before}`}
|
|
255
478
|
ORDER BY run.id DESC LIMIT ${parsedLimit};
|
|
256
479
|
`);
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
480
|
+
return rows.map(({
|
|
481
|
+
isBaseline,
|
|
482
|
+
...run
|
|
483
|
+
}) => ({
|
|
484
|
+
...run,
|
|
485
|
+
isBaseline: isBaseline === 1
|
|
486
|
+
}));
|
|
262
487
|
}));
|
|
263
488
|
});
|
|
264
|
-
const listVariantBaselines = _effect.Effect.fnUntraced(function* (
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
FROM (
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
LEFT JOIN ${sql(_testRepositoryMigrations.tables.testRuns)} run ON run.id = baseline.testRun
|
|
274
|
-
ORDER BY variants.variant;
|
|
275
|
-
`);
|
|
276
|
-
return variants.map(({
|
|
489
|
+
const listVariantBaselines = name => withSnapshot([], _effect.Effect.fnUntraced(function* (sql) {
|
|
490
|
+
const rows = yield* decodeVariantBaselines(yield* sql`
|
|
491
|
+
SELECT variants.variant, run.id, run.name, run.state FROM (
|
|
492
|
+
SELECT DISTINCT variant FROM ${sql(_testRepositorySchema.tables.testRuns)} WHERE name = ${name}
|
|
493
|
+
) variants LEFT JOIN ${sql(_testRepositorySchema.tables.testBaselines)} baseline
|
|
494
|
+
ON baseline.name = ${name} AND baseline.variant = variants.variant
|
|
495
|
+
LEFT JOIN ${sql(_testRepositorySchema.tables.testRuns)} run ON run.id = baseline.testRun ORDER BY variants.variant;
|
|
496
|
+
`);
|
|
497
|
+
return rows.map(({
|
|
277
498
|
variant,
|
|
278
499
|
id,
|
|
279
|
-
name
|
|
280
|
-
state
|
|
500
|
+
name
|
|
281
501
|
}) => ({
|
|
282
502
|
variant,
|
|
283
|
-
baseline: id === null || name === null
|
|
503
|
+
baseline: id === null || name === null ? _effect.Option.none() : _effect.Option.some(_TestRun.TestRun.make({
|
|
284
504
|
id,
|
|
285
505
|
name,
|
|
286
506
|
variant,
|
|
287
|
-
|
|
507
|
+
store: 'snapshot',
|
|
508
|
+
state: 'Complete'
|
|
288
509
|
}))
|
|
289
510
|
}));
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
|
|
511
|
+
}));
|
|
512
|
+
const repository = {
|
|
513
|
+
commitTestRuns,
|
|
293
514
|
getTestRun,
|
|
294
515
|
getLatestTestRun,
|
|
295
516
|
getBaselineTestRun,
|
|
@@ -297,9 +518,14 @@ const makeTestRepository = exports.makeTestRepository = /*#__PURE__*/_effect.Eff
|
|
|
297
518
|
listVariantBaselines,
|
|
298
519
|
getTestResultsStream,
|
|
299
520
|
withTestRun,
|
|
300
|
-
withStats: use => (0, _testResultStats.withStats)(use).pipe(_effect.Effect.provideService(_sql.SqlClient.SqlClient,
|
|
521
|
+
withStats: use => (0, _testResultStats.withStats)(use).pipe(_effect.Effect.provideService(_sql.SqlClient.SqlClient, cache)),
|
|
301
522
|
insertTestResult: (result, run, schemas) => insertTestResults([result], run, schemas),
|
|
302
523
|
insertTestResults
|
|
303
524
|
};
|
|
525
|
+
return {
|
|
526
|
+
repository,
|
|
527
|
+
cache,
|
|
528
|
+
snapshot: getMemorySnapshot
|
|
529
|
+
};
|
|
304
530
|
});
|
|
305
531
|
//# sourceMappingURL=Test.repository.sqlite.js.map
|