@creative-introvert/tons-of-tests 2.0.0 → 3.0.0-rc.0
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/README.md +263 -134
- package/dist/cjs/Classify.js +6 -2
- package/dist/cjs/Classify.js.map +1 -1
- package/dist/cjs/RepositoryError.js +24 -33
- package/dist/cjs/RepositoryError.js.map +1 -1
- package/dist/cjs/Show.js +18 -0
- package/dist/cjs/Show.js.map +1 -1
- package/dist/cjs/Test.js +41 -2
- package/dist/cjs/Test.js.map +1 -1
- package/dist/cjs/Test.repository.js +8 -17
- package/dist/cjs/Test.repository.js.map +1 -1
- package/dist/cjs/TestRun.js +20 -0
- package/dist/cjs/TestRun.js.map +1 -0
- package/dist/cjs/TestValueError.js +23 -0
- package/dist/cjs/TestValueError.js.map +1 -0
- package/dist/cjs/index.js +0 -7
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/Classify.js +1 -9
- package/dist/cjs/internal/Classify.js.map +1 -1
- package/dist/cjs/internal/Test.js +305 -109
- package/dist/cjs/internal/Test.js.map +1 -1
- package/dist/cjs/internal/Test.repository.sqlite.js +266 -304
- package/dist/cjs/internal/Test.repository.sqlite.js.map +1 -1
- package/dist/cjs/internal/Test.serialization.js +128 -0
- package/dist/cjs/internal/Test.serialization.js.map +1 -0
- package/dist/cjs/internal/common.js.map +1 -1
- package/dist/cjs/internal/stats.js +37 -3
- package/dist/cjs/internal/stats.js.map +1 -1
- package/dist/cjs/internal/summarize.js +155 -36
- package/dist/cjs/internal/summarize.js.map +1 -1
- package/dist/cjs/internal/test-repository-fixtures.js +31 -0
- package/dist/cjs/internal/test-repository-fixtures.js.map +1 -0
- package/dist/cjs/internal/test-repository-migrations.js +68 -0
- package/dist/cjs/internal/test-repository-migrations.js.map +1 -0
- package/dist/cjs/internal/test-result-stats.js +89 -0
- package/dist/cjs/internal/test-result-stats.js.map +1 -0
- package/dist/dts/Classify.d.ts +4 -1
- package/dist/dts/Classify.d.ts.map +1 -1
- package/dist/dts/RepositoryError.d.ts +19 -20
- package/dist/dts/RepositoryError.d.ts.map +1 -1
- package/dist/dts/Show.d.ts +8 -8
- package/dist/dts/Show.d.ts.map +1 -1
- package/dist/dts/Test.d.ts +155 -11
- package/dist/dts/Test.d.ts.map +1 -1
- package/dist/dts/Test.repository.d.ts +94 -8
- package/dist/dts/Test.repository.d.ts.map +1 -1
- package/dist/dts/TestRun.d.ts +18 -0
- package/dist/dts/TestRun.d.ts.map +1 -0
- package/dist/dts/TestValueError.d.ts +17 -0
- package/dist/dts/TestValueError.d.ts.map +1 -0
- package/dist/dts/index.d.ts +0 -1
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/Classify.d.ts +0 -1
- package/dist/dts/internal/Classify.d.ts.map +1 -1
- package/dist/dts/internal/Test.d.ts +58 -31
- package/dist/dts/internal/Test.d.ts.map +1 -1
- package/dist/dts/internal/Test.repository.sqlite.d.ts +66 -11
- package/dist/dts/internal/Test.repository.sqlite.d.ts.map +1 -1
- package/dist/dts/internal/Test.serialization.d.ts +34 -0
- package/dist/dts/internal/Test.serialization.d.ts.map +1 -0
- package/dist/dts/internal/common.d.ts +1 -1
- package/dist/dts/internal/common.d.ts.map +1 -1
- package/dist/dts/internal/stats.d.ts +15 -1
- package/dist/dts/internal/stats.d.ts.map +1 -1
- package/dist/dts/internal/summarize.d.ts +39 -7
- package/dist/dts/internal/summarize.d.ts.map +1 -1
- package/dist/dts/internal/test-repository-fixtures.d.ts +22 -0
- package/dist/dts/internal/test-repository-fixtures.d.ts.map +1 -0
- package/dist/dts/internal/test-repository-migrations.d.ts +11 -0
- package/dist/dts/internal/test-repository-migrations.d.ts.map +1 -0
- package/dist/dts/internal/test-result-stats.d.ts +7 -0
- package/dist/dts/internal/test-result-stats.d.ts.map +1 -0
- package/dist/esm/Classify.js +5 -1
- package/dist/esm/Classify.js.map +1 -1
- package/dist/esm/RepositoryError.js +14 -29
- package/dist/esm/RepositoryError.js.map +1 -1
- package/dist/esm/Show.js +2 -2
- package/dist/esm/Show.js.map +1 -1
- package/dist/esm/Test.js +38 -1
- package/dist/esm/Test.js.map +1 -1
- package/dist/esm/Test.repository.js +7 -16
- package/dist/esm/Test.repository.js.map +1 -1
- package/dist/esm/TestRun.js +13 -0
- package/dist/esm/TestRun.js.map +1 -0
- package/dist/esm/TestValueError.js +16 -0
- package/dist/esm/TestValueError.js.map +1 -0
- package/dist/esm/index.js +0 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/Classify.js +0 -7
- package/dist/esm/internal/Classify.js.map +1 -1
- package/dist/esm/internal/Test.js +299 -108
- package/dist/esm/internal/Test.js.map +1 -1
- package/dist/esm/internal/Test.repository.sqlite.js +265 -301
- package/dist/esm/internal/Test.repository.sqlite.js.map +1 -1
- package/dist/esm/internal/Test.serialization.js +119 -0
- package/dist/esm/internal/Test.serialization.js.map +1 -0
- package/dist/esm/internal/common.js.map +1 -1
- package/dist/esm/internal/stats.js +35 -2
- package/dist/esm/internal/stats.js.map +1 -1
- package/dist/esm/internal/summarize.js +155 -35
- package/dist/esm/internal/summarize.js.map +1 -1
- package/dist/esm/internal/test-repository-fixtures.js +25 -0
- package/dist/esm/internal/test-repository-fixtures.js.map +1 -0
- package/dist/esm/internal/test-repository-migrations.js +62 -0
- package/dist/esm/internal/test-repository-migrations.js.map +1 -0
- package/dist/esm/internal/test-result-stats.js +83 -0
- package/dist/esm/internal/test-result-stats.js.map +1 -0
- package/package.json +1 -1
- package/dist/cjs/Test.repository.types.js +0 -14
- package/dist/cjs/Test.repository.types.js.map +0 -1
- package/dist/cjs/internal/lib/Schema.js +0 -14
- package/dist/cjs/internal/lib/Schema.js.map +0 -1
- package/dist/dts/Test.repository.types.d.ts +0 -56
- package/dist/dts/Test.repository.types.d.ts.map +0 -1
- package/dist/dts/internal/lib/Schema.d.ts +0 -4
- package/dist/dts/internal/lib/Schema.d.ts.map +0 -1
- package/dist/esm/Test.repository.types.js +0 -7
- package/dist/esm/Test.repository.types.js.map +0 -1
- package/dist/esm/internal/lib/Schema.js +0 -7
- package/dist/esm/internal/lib/Schema.js.map +0 -1
|
@@ -1,330 +1,294 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
export { TestRepository };
|
|
6
|
-
import { RepositoryDecodeError, RepositoryQueryError, RepositoryWriteError, TestRunNotCreated } from '../RepositoryError.js';
|
|
1
|
+
import { Array as A, Effect, Exit, Option, Schema, Stream } from 'effect';
|
|
2
|
+
import { SqlClient } from 'effect/sql';
|
|
3
|
+
import { TestRun } from '../TestRun.js';
|
|
4
|
+
import { TestRunNotCreated, TestRunIncomplete, TestRunNotFound } from '../RepositoryError.js';
|
|
7
5
|
import { LabelSchema } from './Classify.js';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
};
|
|
14
|
-
const TestResultWriteSchema = /*#__PURE__*/Schema.Struct({
|
|
6
|
+
import { migrate, tables } from './test-repository-migrations.js';
|
|
7
|
+
import { withStats } from './test-result-stats.js';
|
|
8
|
+
import { makeResultCodec, stringifyJson } from './Test.serialization.js';
|
|
9
|
+
export { tables } from './test-repository-migrations.js';
|
|
10
|
+
export const TestResultStorage = /*#__PURE__*/Schema.Struct({
|
|
15
11
|
id: Schema.String,
|
|
16
12
|
hashTestCase: Schema.String,
|
|
13
|
+
key: Schema.String,
|
|
14
|
+
input: /*#__PURE__*/Schema.fromJsonString(Schema.Json),
|
|
15
|
+
result: /*#__PURE__*/Schema.fromJsonString(Schema.Json),
|
|
16
|
+
expected: /*#__PURE__*/Schema.fromJsonString(Schema.Json),
|
|
17
|
+
label: LabelSchema
|
|
18
|
+
});
|
|
19
|
+
const StoredResult = /*#__PURE__*/Schema.Struct({
|
|
20
|
+
...TestResultStorage.fields,
|
|
21
|
+
input: Schema.String,
|
|
22
|
+
result: Schema.String,
|
|
23
|
+
expected: Schema.String,
|
|
17
24
|
ordering: Schema.Int,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
expected: /*#__PURE__*/Schema.fromJsonString(Schema.Unknown),
|
|
21
|
-
label: LabelSchema,
|
|
22
|
-
tags: /*#__PURE__*/split(','),
|
|
25
|
+
resultOrdering: Schema.Int,
|
|
26
|
+
tags: /*#__PURE__*/Schema.fromJsonString(/*#__PURE__*/Schema.Array(Schema.String)),
|
|
23
27
|
timeMillis: Schema.Finite
|
|
24
28
|
});
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
const encodeResult = /*#__PURE__*/Schema.encodeEffect(StoredResult);
|
|
30
|
+
const decodeRows = /*#__PURE__*/Schema.decodeUnknownEffect(/*#__PURE__*/Schema.Array(StoredResult));
|
|
31
|
+
const decodeRuns = /*#__PURE__*/Schema.decodeUnknownEffect(/*#__PURE__*/Schema.Array(TestRun));
|
|
32
|
+
const decodeListedRuns = /*#__PURE__*/Schema.decodeUnknownEffect(/*#__PURE__*/Schema.Array(/*#__PURE__*/Schema.Struct({
|
|
33
|
+
...TestRun.fields,
|
|
34
|
+
isBaseline: /*#__PURE__*/Schema.Literals([0, 1])
|
|
35
|
+
})));
|
|
36
|
+
const decodeVariantBaselines = /*#__PURE__*/Schema.decodeUnknownEffect(/*#__PURE__*/Schema.Array(/*#__PURE__*/Schema.Struct({
|
|
37
|
+
variant: Schema.String,
|
|
38
|
+
id: /*#__PURE__*/Schema.NullOr(TestRun.fields.id),
|
|
39
|
+
name: /*#__PURE__*/Schema.NullOr(TestRun.fields.name),
|
|
40
|
+
state: /*#__PURE__*/Schema.NullOr(TestRun.fields.state)
|
|
41
|
+
})));
|
|
42
|
+
const decodeRunLimit = /*#__PURE__*/Schema.decodeUnknownEffect(/*#__PURE__*/Schema.Int.check(/*#__PURE__*/Schema.isBetween({
|
|
43
|
+
minimum: 1,
|
|
44
|
+
maximum: 100
|
|
35
45
|
})));
|
|
36
|
-
const TestRunsReadSchema = /*#__PURE__*/Schema.Array(TestRun);
|
|
37
|
-
const TestRunResultsSchema = /*#__PURE__*/Schema.Array(/*#__PURE__*/Schema.Struct({
|
|
38
|
-
testRun: Schema.Int,
|
|
39
|
-
testResult: Schema.String
|
|
40
|
-
}));
|
|
41
46
|
export const makeTestRepository = /*#__PURE__*/Effect.gen(function* () {
|
|
42
47
|
const sql = yield* SqlClient.SqlClient;
|
|
43
48
|
yield* sql`PRAGMA auto_vacuum = FULL;`;
|
|
44
|
-
yield*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// cleanup (`... NOT IN (SELECT testResult ...)`) and stream joins
|
|
84
|
-
// (`res.id = runres.testResult`) would otherwise full-scan the junction.
|
|
85
|
-
yield* sql`CREATE INDEX IF NOT EXISTS idx_trr_testresult ON ${sql(tables.testRunResults)}(testResult)`;
|
|
86
|
-
yield* sql`CREATE INDEX IF NOT EXISTS idx_tr_hashtestcase ON ${sql(tables.testResults)}(hashTestCase)`;
|
|
87
|
-
// SQLite's default SQLITE_MAX_VARIABLE_NUMBER is 999; testResults has 9
|
|
88
|
-
// columns and junction rows 2, so 50 rows stays well under the limit.
|
|
89
|
-
const INSERT_BATCH_SIZE = 50;
|
|
90
|
-
const insertTestResults = (testResults, testRun) => sql.withTransaction(Effect.gen(function* () {
|
|
91
|
-
if (testResults.length === 0) return;
|
|
92
|
-
const encoded = yield* Effect.forEach(testResults, _ => Schema.encodeEffect(TestResultWriteSchema)(_).pipe(Effect.mapError(RepositoryDecodeError.from('insertTestResults'))));
|
|
93
|
-
for (let i = 0; i < encoded.length; i += INSERT_BATCH_SIZE) {
|
|
94
|
-
const chunk = encoded.slice(i, i + INSERT_BATCH_SIZE);
|
|
95
|
-
yield* sql`
|
|
96
|
-
INSERT INTO ${sql(tables.testResults)}
|
|
97
|
-
${sql.insert(chunk)}
|
|
49
|
+
yield* migrate;
|
|
50
|
+
const getTestRun = Effect.fnUntraced(function* (id) {
|
|
51
|
+
return A.head(yield* decodeRuns(yield* sql`SELECT * FROM ${sql(tables.testRuns)} WHERE id = ${id};`));
|
|
52
|
+
});
|
|
53
|
+
const insertTestResults = Effect.fnUntraced(function* (results, run, schemas) {
|
|
54
|
+
const codec = makeResultCodec(schemas, run.name);
|
|
55
|
+
// Each statement stays below SQLite's lowest default limit of 999 parameters.
|
|
56
|
+
for (let offset = 0; offset < Math.max(1, results.length); offset += 50) {
|
|
57
|
+
const encoded = yield* Effect.forEach(results.slice(offset, offset + 50), Effect.fnUntraced(function* (result) {
|
|
58
|
+
const encoded = yield* codec.encode(result);
|
|
59
|
+
return yield* encodeResult({
|
|
60
|
+
...encoded,
|
|
61
|
+
input: stringifyJson(encoded.input),
|
|
62
|
+
result: stringifyJson(encoded.result),
|
|
63
|
+
expected: stringifyJson(encoded.expected)
|
|
64
|
+
});
|
|
65
|
+
}));
|
|
66
|
+
yield* sql.withTransaction(Effect.gen(function* () {
|
|
67
|
+
const saved = yield* getTestRun(run.id);
|
|
68
|
+
if (Option.isNone(saved)) {
|
|
69
|
+
return yield* new TestRunNotFound({
|
|
70
|
+
testRunId: run.id
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (saved.value.state !== 'Running') {
|
|
74
|
+
return yield* Effect.die(new Error('Only Running test runs accept result writes.'));
|
|
75
|
+
}
|
|
76
|
+
if (encoded.length === 0) return;
|
|
77
|
+
const content = encoded.map(result => ({
|
|
78
|
+
id: result.id,
|
|
79
|
+
hashTestCase: result.hashTestCase,
|
|
80
|
+
key: result.key,
|
|
81
|
+
input: result.input,
|
|
82
|
+
result: result.result,
|
|
83
|
+
expected: result.expected,
|
|
84
|
+
label: result.label
|
|
85
|
+
}));
|
|
86
|
+
yield* sql`
|
|
87
|
+
INSERT INTO ${sql(tables.testResults)} ${sql.insert(content)}
|
|
98
88
|
ON CONFLICT(id) DO NOTHING;
|
|
99
|
-
|
|
89
|
+
`;
|
|
90
|
+
const links = encoded.map(result => ({
|
|
91
|
+
testRun: run.id,
|
|
92
|
+
testResult: result.id,
|
|
93
|
+
ordering: result.ordering,
|
|
94
|
+
resultOrdering: result.resultOrdering,
|
|
95
|
+
timeMillis: result.timeMillis,
|
|
96
|
+
tags: result.tags
|
|
97
|
+
}));
|
|
98
|
+
yield* sql`
|
|
99
|
+
INSERT INTO ${sql(tables.testRunResults)} ${sql.insert(links)}
|
|
100
|
+
ON CONFLICT(testRun, testResult) DO UPDATE SET
|
|
101
|
+
ordering = excluded.ordering,
|
|
102
|
+
resultOrdering = excluded.resultOrdering,
|
|
103
|
+
timeMillis = excluded.timeMillis,
|
|
104
|
+
tags = excluded.tags;
|
|
105
|
+
`;
|
|
106
|
+
}));
|
|
100
107
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
testResult: _.id
|
|
106
|
-
}));
|
|
107
|
-
for (let i = 0; i < junction.length; i += INSERT_BATCH_SIZE) {
|
|
108
|
-
const chunk = junction.slice(i, i + INSERT_BATCH_SIZE);
|
|
109
|
-
yield* sql`
|
|
110
|
-
INSERT INTO ${sql(tables.testRunResults)}
|
|
111
|
-
${sql.insert(chunk)}
|
|
112
|
-
ON CONFLICT(testRun, testResult) DO NOTHING;
|
|
113
|
-
`.pipe(Effect.mapError(RepositoryWriteError.from('insertTestResults')));
|
|
108
|
+
});
|
|
109
|
+
const getTestResultsStream = (run, schemas, matching) => {
|
|
110
|
+
if (matching !== undefined && matching.length > 50) {
|
|
111
|
+
return Stream.die(new Error('Match at most 50 case/key pairs per query.'));
|
|
114
112
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
ORDER BY ordering ASC;
|
|
145
|
-
`.pipe(Effect.mapError(RepositoryQueryError.fromSql('getTestResultsStream')));
|
|
146
|
-
const decoded = yield* Schema.decodeUnknownEffect(RowsSchema)(rows).pipe(Effect.mapError(RepositoryDecodeError.from('getTestResultsStream')));
|
|
147
|
-
return Stream.fromIterable(decoded);
|
|
113
|
+
const codec = makeResultCodec(schemas, run.name);
|
|
114
|
+
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}`));
|
|
115
|
+
// Match case/key pairs first, so each batch avoids scanning the whole prior run.
|
|
116
|
+
// CROSS JOIN keeps SQLite from choosing the run-order index for that lookup.
|
|
117
|
+
const from = matching === undefined ? sql`${sql(tables.testRunResults)} runres
|
|
118
|
+
JOIN ${sql(tables.testResults)} res ON res.id = runres.testResult` : sql`${sql(tables.testResults)} res
|
|
119
|
+
CROSS JOIN ${sql(tables.testRunResults)} runres ON res.id = runres.testResult`;
|
|
120
|
+
// The driver cannot stream a query. Keyset pages keep decoded rows bounded.
|
|
121
|
+
return Stream.paginate(undefined, Effect.fnUntraced(function* (after) {
|
|
122
|
+
const rows = yield* decodeRows(yield* sql`
|
|
123
|
+
SELECT res.id, res.hashTestCase, res.key,
|
|
124
|
+
runres.ordering, runres.resultOrdering, res.input, res.result,
|
|
125
|
+
res.expected, res.label, runres.tags, runres.timeMillis
|
|
126
|
+
FROM ${from}
|
|
127
|
+
WHERE runres.testRun = ${run.id} AND ${filter}
|
|
128
|
+
AND ${after === undefined ? sql`1 = 1` : sql`
|
|
129
|
+
(runres.ordering, runres.resultOrdering, runres.testResult) >
|
|
130
|
+
(${after.ordering}, ${after.resultOrdering}, ${after.id})
|
|
131
|
+
`}
|
|
132
|
+
ORDER BY runres.ordering, runres.resultOrdering, runres.testResult
|
|
133
|
+
LIMIT 100;
|
|
134
|
+
`);
|
|
135
|
+
const last = rows[rows.length - 1];
|
|
136
|
+
const decoded = yield* Effect.forEach(rows, codec.decode);
|
|
137
|
+
return [decoded, rows.length < 100 ? Option.none() : Option.some({
|
|
138
|
+
ordering: last.ordering,
|
|
139
|
+
resultOrdering: last.resultOrdering,
|
|
140
|
+
id: last.id
|
|
141
|
+
})];
|
|
148
142
|
}));
|
|
149
143
|
};
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const r = yield* sql`
|
|
160
|
-
SELECT * FROM ${sql(tables.testRuns)}
|
|
161
|
-
WHERE hash IS NOT NULL AND name = ${name}
|
|
162
|
-
ORDER BY id DESC
|
|
163
|
-
LIMIT 1;
|
|
164
|
-
`.pipe(Effect.mapError(RepositoryQueryError.fromSql('getPreviousTestRun')));
|
|
165
|
-
const p = yield* Schema.decodeUnknownEffect(TestRunsReadSchema)(r).pipe(Effect.mapError(RepositoryDecodeError.from('getPreviousTestRun')));
|
|
166
|
-
return A.head(p);
|
|
167
|
-
});
|
|
168
|
-
const getAllTestRuns = Effect.gen(function* () {
|
|
169
|
-
const results = yield* sql`
|
|
170
|
-
SELECT * from ${sql(tables.testRuns)};
|
|
171
|
-
`.pipe(Effect.mapError(RepositoryQueryError.fromSql('getAllTestRuns')));
|
|
172
|
-
return yield* Schema.decodeUnknownEffect(TestRunsReadSchema)(results).pipe(Effect.mapError(RepositoryDecodeError.from('getAllTestRuns')));
|
|
173
|
-
});
|
|
174
|
-
const getAllTestRunResults = Effect.gen(function* () {
|
|
175
|
-
const results = yield* sql`
|
|
176
|
-
SELECT * from ${sql(tables.testRunResults)};
|
|
177
|
-
`.pipe(Effect.mapError(RepositoryQueryError.fromSql('getAllTestRunResults')));
|
|
178
|
-
return yield* Schema.decodeUnknownEffect(TestRunResultsSchema)(results).pipe(Effect.mapError(RepositoryDecodeError.from('getAllTestRunResults')));
|
|
179
|
-
});
|
|
180
|
-
const getAllTestResults = Effect.gen(function* () {
|
|
181
|
-
const results = yield* sql`
|
|
182
|
-
SELECT * from ${sql(tables.testResults)};
|
|
183
|
-
`.pipe(Effect.mapError(RepositoryQueryError.fromSql('getAllTestResults')));
|
|
184
|
-
return yield* Schema.decodeUnknownEffect(TestResultsSchema)(results).pipe(Effect.mapError(RepositoryDecodeError.from('getAllTestResults')));
|
|
144
|
+
const getLatestTestRun = Effect.fnUntraced(function* ({
|
|
145
|
+
name,
|
|
146
|
+
variant
|
|
147
|
+
}) {
|
|
148
|
+
return A.head(yield* decodeRuns(yield* sql`
|
|
149
|
+
SELECT * FROM ${sql(tables.testRuns)}
|
|
150
|
+
WHERE name = ${name} AND variant = ${variant}
|
|
151
|
+
ORDER BY id DESC LIMIT 1;
|
|
152
|
+
`));
|
|
185
153
|
});
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return A.head(runs).pipe(Option.flatMap(run => Option.fromNullishOr(run.hash)));
|
|
154
|
+
const getBaselineTestRun = Effect.fnUntraced(function* ({
|
|
155
|
+
name,
|
|
156
|
+
variant
|
|
157
|
+
}) {
|
|
158
|
+
return A.head(yield* decodeRuns(yield* sql`
|
|
159
|
+
SELECT run.* FROM ${sql(tables.testBaselines)} baseline
|
|
160
|
+
JOIN ${sql(tables.testRuns)} run ON run.id = baseline.testRun
|
|
161
|
+
WHERE baseline.name = ${name} AND baseline.variant = ${variant};
|
|
162
|
+
`));
|
|
196
163
|
});
|
|
197
|
-
const
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const current = yield* Schema.decodeUnknownEffect(TestRunsReadSchema)(currentRaw).pipe(Effect.mapError(RepositoryDecodeError.from('getOrCreateCurrentTestRun')));
|
|
206
|
-
const existing = A.head(current);
|
|
207
|
-
if (Option.isSome(existing)) {
|
|
208
|
-
return existing.value;
|
|
209
|
-
}
|
|
210
|
-
const insertedRaw = yield* sql`
|
|
211
|
-
INSERT INTO ${sql(tables.testRuns)}
|
|
212
|
-
${sql.insert({
|
|
213
|
-
name
|
|
214
|
-
})}
|
|
215
|
-
RETURNING *;
|
|
216
|
-
`.pipe(Effect.mapError(RepositoryWriteError.from('getOrCreateCurrentTestRun')));
|
|
217
|
-
const inserted = yield* Schema.decodeUnknownEffect(TestRunsReadSchema)(insertedRaw).pipe(Effect.mapError(RepositoryDecodeError.from('getOrCreateCurrentTestRun')));
|
|
164
|
+
const createTestRun = Effect.fnUntraced(function* (input) {
|
|
165
|
+
const inserted = yield* decodeRuns(yield* sql`
|
|
166
|
+
INSERT INTO ${sql(tables.testRuns)}
|
|
167
|
+
${sql.insert({
|
|
168
|
+
...input,
|
|
169
|
+
state: 'Running'
|
|
170
|
+
})} RETURNING *;
|
|
171
|
+
`);
|
|
218
172
|
return yield* Effect.fromOption(A.head(inserted)).pipe(Effect.mapError(() => new TestRunNotCreated({
|
|
219
|
-
testSuiteName: name
|
|
173
|
+
testSuiteName: input.name
|
|
220
174
|
})));
|
|
221
|
-
}
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
LIMIT -1 OFFSET ${keep};
|
|
232
|
-
`.pipe(Effect.mapError(RepositoryQueryError.fromSql('clearStale')));
|
|
233
|
-
const staleTestRuns = yield* Schema.decodeUnknownEffect(TestRunsReadSchema)(raw).pipe(Effect.mapError(RepositoryDecodeError.from('clearStale')));
|
|
234
|
-
if (staleTestRuns.length === 0) {
|
|
235
|
-
return;
|
|
175
|
+
}, sql.withTransaction);
|
|
176
|
+
const finishTestRun = Effect.fnUntraced(function* (run, state) {
|
|
177
|
+
const updated = A.head(yield* decodeRuns(yield* sql`
|
|
178
|
+
UPDATE ${sql(tables.testRuns)} SET state = ${state}
|
|
179
|
+
WHERE id = ${run.id} AND state = 'Running' RETURNING *;
|
|
180
|
+
`));
|
|
181
|
+
if (Option.isNone(updated)) {
|
|
182
|
+
return yield* new TestRunNotFound({
|
|
183
|
+
testRunId: run.id
|
|
184
|
+
});
|
|
236
185
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return;
|
|
186
|
+
}, sql.withTransaction);
|
|
187
|
+
const withTestRun = (input, execute) => Effect.acquireUseRelease(createTestRun(input), run => Effect.suspend(() => execute(run)).pipe(Effect.map(value => ({
|
|
188
|
+
run,
|
|
189
|
+
value
|
|
190
|
+
}))), (run, exit) => finishTestRun(run, Exit.isSuccess(exit) ? 'Complete' : 'Failed')).pipe(Effect.map(({
|
|
191
|
+
run,
|
|
192
|
+
value
|
|
193
|
+
}) => ({
|
|
194
|
+
run: new TestRun({
|
|
195
|
+
id: run.id,
|
|
196
|
+
name: run.name,
|
|
197
|
+
variant: run.variant,
|
|
198
|
+
state: 'Complete'
|
|
199
|
+
}),
|
|
200
|
+
value
|
|
201
|
+
})));
|
|
202
|
+
const commitTestRun = Effect.fnUntraced(function* (id) {
|
|
203
|
+
const found = yield* getTestRun(id);
|
|
204
|
+
if (Option.isNone(found)) {
|
|
205
|
+
return yield* new TestRunNotFound({
|
|
206
|
+
testRunId: id
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
const run = found.value;
|
|
210
|
+
if (run.state !== 'Complete') {
|
|
211
|
+
return yield* new TestRunIncomplete({
|
|
212
|
+
testSuiteName: run.name,
|
|
213
|
+
testRunId: run.id,
|
|
214
|
+
variant: run.variant
|
|
215
|
+
});
|
|
268
216
|
}
|
|
269
217
|
yield* sql`
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
const commitCurrentTestRun = Effect.fnUntraced(function* ({
|
|
218
|
+
INSERT INTO ${sql(tables.testBaselines)}
|
|
219
|
+
${sql.insert({
|
|
220
|
+
name: run.name,
|
|
221
|
+
variant: run.variant,
|
|
222
|
+
testRun: run.id
|
|
223
|
+
})}
|
|
224
|
+
ON CONFLICT(name, variant) DO UPDATE SET testRun = excluded.testRun;
|
|
225
|
+
`;
|
|
226
|
+
}, sql.withTransaction);
|
|
227
|
+
const listTestRuns = Effect.fnUntraced(function* ({
|
|
281
228
|
name,
|
|
282
|
-
|
|
229
|
+
variant,
|
|
230
|
+
before,
|
|
231
|
+
limit
|
|
283
232
|
}) {
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
yield* sql`
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
233
|
+
const parsedLimit = yield* decodeRunLimit(limit);
|
|
234
|
+
const runs = yield* decodeListedRuns(yield* sql`
|
|
235
|
+
SELECT run.*, EXISTS(
|
|
236
|
+
SELECT 1 FROM ${sql(tables.testBaselines)} baseline
|
|
237
|
+
WHERE baseline.name = run.name AND baseline.variant = run.variant
|
|
238
|
+
AND baseline.testRun = run.id
|
|
239
|
+
) AS isBaseline
|
|
240
|
+
FROM ${sql(tables.testRuns)} run
|
|
241
|
+
WHERE run.name = ${name}
|
|
242
|
+
AND ${variant === undefined ? sql`1 = 1` : sql`run.variant = ${variant}`}
|
|
243
|
+
AND ${before === undefined ? sql`1 = 1` : sql`run.id < ${before}`}
|
|
244
|
+
ORDER BY run.id DESC LIMIT ${parsedLimit};
|
|
245
|
+
`);
|
|
246
|
+
return runs.map(({
|
|
247
|
+
isBaseline,
|
|
248
|
+
...run
|
|
249
|
+
}) => Object.assign(new TestRun(run), {
|
|
250
|
+
isBaseline: isBaseline === 1
|
|
251
|
+
}));
|
|
252
|
+
});
|
|
253
|
+
const listVariantBaselines = Effect.fnUntraced(function* (name) {
|
|
254
|
+
const variants = yield* decodeVariantBaselines(yield* sql`
|
|
255
|
+
SELECT variants.variant, run.id, run.name, run.state
|
|
256
|
+
FROM (
|
|
257
|
+
SELECT DISTINCT variant FROM ${sql(tables.testRuns)}
|
|
258
|
+
WHERE name = ${name}
|
|
259
|
+
) variants
|
|
260
|
+
LEFT JOIN ${sql(tables.testBaselines)} baseline
|
|
261
|
+
ON baseline.name = ${name} AND baseline.variant = variants.variant
|
|
262
|
+
LEFT JOIN ${sql(tables.testRuns)} run ON run.id = baseline.testRun
|
|
263
|
+
ORDER BY variants.variant;
|
|
264
|
+
`);
|
|
265
|
+
return variants.map(({
|
|
266
|
+
variant,
|
|
267
|
+
id,
|
|
268
|
+
name,
|
|
269
|
+
state
|
|
270
|
+
}) => ({
|
|
271
|
+
variant,
|
|
272
|
+
baseline: id === null || name === null || state === null ? Option.none() : Option.some(new TestRun({
|
|
273
|
+
id,
|
|
274
|
+
name,
|
|
275
|
+
variant,
|
|
276
|
+
state
|
|
277
|
+
}))
|
|
278
|
+
}));
|
|
312
279
|
});
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
getLastTestRunHash,
|
|
321
|
-
getPreviousTestRun,
|
|
280
|
+
return {
|
|
281
|
+
commitTestRun,
|
|
282
|
+
getTestRun,
|
|
283
|
+
getLatestTestRun,
|
|
284
|
+
getBaselineTestRun,
|
|
285
|
+
listTestRuns,
|
|
286
|
+
listVariantBaselines,
|
|
322
287
|
getTestResultsStream,
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
insertTestResults,
|
|
326
|
-
|
|
288
|
+
withTestRun,
|
|
289
|
+
withStats: use => withStats(use).pipe(Effect.provideService(SqlClient.SqlClient, sql)),
|
|
290
|
+
insertTestResult: (result, run, schemas) => insertTestResults([result], run, schemas),
|
|
291
|
+
insertTestResults
|
|
327
292
|
};
|
|
328
|
-
return service;
|
|
329
293
|
});
|
|
330
294
|
//# sourceMappingURL=Test.repository.sqlite.js.map
|