import { Effect, Schema } from 'effect';
import { describe, expect, it } from 'vitest';
import { Ulid } from '../../../core/index.js';
import { EntityESchema, ESchema } from '../../../eschema/index.js';
import { StdTable } from '../table/index.js';
import { OperationFailure, contractLayer } from '../contract/index.js';
import { makeDeterministicContract } from './deterministic-contract.js';
const table = StdTable.make('op-construction').primary('pk', 'sk').build();
const schema = EntityESchema.make('Task', 'taskId', {
bucket: Schema.String,
label: Schema.String,
}).build();
const task = table
.entity(schema)
.primary({ pk: ['bucket'] })
.build();
const settings = table
.singleEntity(ESchema.make('Settings', { theme: Schema.String }).build())
.default({ theme: 'light' });
const key = { taskId: 't1', bucket: 'inbox' };
const value = { ...key, label: 'Write it down' };
/**
* Building an op must not touch the database. The rule is invisible in a diff,
* so it is pinned here: every constructor runs against a contract whose reads
* throw, and every one of them must still produce an op.
*/
describe('transact ops carry intent only', () => {
const deterministic = makeDeterministicContract(table.logicalName);
let reads = 0;
const contract = {
...deterministic.contract,
getItem: () => {
reads++;
return Effect.fail(
new OperationFailure({
cause: new Error('An op constructor must not read'),
}),
);
},
};
const run = (effect: Effect.Effect) =>
Effect.runPromise(
(effect as Effect.Effect).pipe(
Effect.provide(contractLayer(table.logicalName, contract)),
Effect.provideService(Ulid, () => '01ARZ3NDEKTSV4RRFFQ69G5FAV'),
) as Effect.Effect,
);
it('builds every keyed op without a read', async () => {
const ops = await run(
Effect.all([
task.insertOp(value),
task.getAndUpdateOp(key, { label: 'changed' }),
task.getAndUpdateOp(key, (current) => ({ label: current.label })),
task.getAndUpdateOp(
key,
{ label: 'checked' },
{ check: (current) => current.label !== '' },
),
task.deleteOp(key),
task.restoreOp(key),
task.getAndCheckOp(key, (current) => current.label !== ''),
task.existsOp(key),
task.notExistsOp(key),
task.unchangedOp({
value,
meta: { _e: 'Task', _u: '01ARZ3NDEKTSV4RRFFQ69G5FAV', _d: false },
}),
]),
);
expect(reads).toBe(0);
expect(ops).toHaveLength(10);
expect(ops.map((op) => op.readsCurrent)).toEqual([
false, // insertOp
true, // getAndUpdateOp
true,
true,
true, // deleteOp
true, // restoreOp
true, // getAndCheckOp
false, // existsOp
false, // notExistsOp
false, // unchangedOp
]);
});
it('builds every single-entity op without a read', async () => {
const before = reads;
const ops = await run(
Effect.all([
settings.getAndUpdateOp({ theme: 'dark' }),
settings.getAndUpdateOp((current) => ({ theme: current.theme })),
settings.getAndUpdateOp(
{ theme: 'checked' },
{ check: (current) => current.theme !== '' },
),
settings.unchangedOp({
value: { theme: 'light' },
meta: { _e: 'Settings', _u: '' },
}),
]),
);
expect(reads).toBe(before);
expect(ops.map((op) => op.readsCurrent)).toEqual([true, true, true, false]);
});
});