@rotorsoft/act-tck 0.4.0 → 1.1.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 +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/store-tck.d.ts +8 -0
- package/dist/@types/store-tck.d.ts.map +1 -1
- package/dist/index.cjs +316 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +320 -5
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/cache-tck.ts
|
|
2
|
-
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { afterEach, beforeEach as beforeEach2, describe, expect, it } from "vitest";
|
|
3
3
|
var entry = (event_id, state = {}) => ({
|
|
4
4
|
state,
|
|
5
5
|
version: event_id,
|
|
@@ -10,7 +10,7 @@ var entry = (event_id, state = {}) => ({
|
|
|
10
10
|
var runCacheTck = (options) => {
|
|
11
11
|
describe(`TCK / Cache / ${options.name}`, () => {
|
|
12
12
|
let cache;
|
|
13
|
-
|
|
13
|
+
beforeEach2(() => {
|
|
14
14
|
cache = options.factory();
|
|
15
15
|
});
|
|
16
16
|
afterEach(async () => {
|
|
@@ -129,13 +129,13 @@ var collect = async (store, query) => {
|
|
|
129
129
|
};
|
|
130
130
|
|
|
131
131
|
// src/logger-tck.ts
|
|
132
|
-
import { afterEach as afterEach2, beforeEach as
|
|
132
|
+
import { afterEach as afterEach2, beforeEach as beforeEach3, describe as describe2, expect as expect2, it as it2 } from "vitest";
|
|
133
133
|
var LEVELS = ["fatal", "error", "warn", "info", "debug", "trace"];
|
|
134
134
|
var runLoggerTck = (options) => {
|
|
135
135
|
describe2(`TCK / Logger / ${options.name}`, () => {
|
|
136
136
|
let logger;
|
|
137
137
|
let originalStdout;
|
|
138
|
-
|
|
138
|
+
beforeEach3(() => {
|
|
139
139
|
logger = options.factory();
|
|
140
140
|
originalStdout = process.stdout.write.bind(process.stdout);
|
|
141
141
|
process.stdout.write = (() => true);
|
|
@@ -194,7 +194,7 @@ import { afterAll, beforeAll, describe as describe3, expect as expect3, it as it
|
|
|
194
194
|
var runStoreTck = (options) => {
|
|
195
195
|
describe3(`TCK / Store / ${options.name}`, () => {
|
|
196
196
|
let store;
|
|
197
|
-
const caps = options.capabilities
|
|
197
|
+
const caps = { ...options.capabilities };
|
|
198
198
|
beforeAll(async () => {
|
|
199
199
|
store = await options.factory();
|
|
200
200
|
await store.drop();
|
|
@@ -1504,6 +1504,321 @@ var runStoreTck = (options) => {
|
|
|
1504
1504
|
}
|
|
1505
1505
|
});
|
|
1506
1506
|
});
|
|
1507
|
+
describe3.skipIf(!caps.restore)("restore (capability)", () => {
|
|
1508
|
+
beforeEach(async () => {
|
|
1509
|
+
await store.drop();
|
|
1510
|
+
await store.seed();
|
|
1511
|
+
});
|
|
1512
|
+
const asSource = (rows) => (async function* () {
|
|
1513
|
+
for (const r of rows) yield r;
|
|
1514
|
+
})();
|
|
1515
|
+
const row = (originalId, stream, version, name, created, data = {}) => ({
|
|
1516
|
+
id: originalId,
|
|
1517
|
+
name,
|
|
1518
|
+
data,
|
|
1519
|
+
stream,
|
|
1520
|
+
version,
|
|
1521
|
+
created,
|
|
1522
|
+
meta: { correlation: "restore-tck", causation: {} }
|
|
1523
|
+
});
|
|
1524
|
+
it3("returns kept=0 on an empty source", async () => {
|
|
1525
|
+
const result = await store.restore(asSource([]));
|
|
1526
|
+
expect3(result.kept).toBe(0);
|
|
1527
|
+
expect3(result.duration_ms).toBeGreaterThanOrEqual(0);
|
|
1528
|
+
expect3(result.dry_run).toBe(false);
|
|
1529
|
+
expect3(result.dropped).toEqual({
|
|
1530
|
+
closed_streams: 0,
|
|
1531
|
+
snapshots: 0,
|
|
1532
|
+
empty_streams: 0
|
|
1533
|
+
});
|
|
1534
|
+
const events = await collect(store, { limit: 10 });
|
|
1535
|
+
expect3(events).toHaveLength(0);
|
|
1536
|
+
});
|
|
1537
|
+
it3("rebuilds a single stream and preserves `created` verbatim", async () => {
|
|
1538
|
+
const s = `restore-single-${uid()}`;
|
|
1539
|
+
const t0 = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
|
|
1540
|
+
const t1 = /* @__PURE__ */ new Date("2020-01-02T00:00:00.000Z");
|
|
1541
|
+
const t2 = /* @__PURE__ */ new Date("2020-01-03T00:00:00.000Z");
|
|
1542
|
+
const rows = [
|
|
1543
|
+
row(1, s, 0, "Incremented", t0, { amount: 1 }),
|
|
1544
|
+
row(2, s, 1, "Incremented", t1, { amount: 2 }),
|
|
1545
|
+
row(3, s, 2, "Decremented", t2, { amount: 1 })
|
|
1546
|
+
];
|
|
1547
|
+
const result = await store.restore(asSource(rows));
|
|
1548
|
+
expect3(result.kept).toBe(3);
|
|
1549
|
+
const back = [];
|
|
1550
|
+
await store.query(
|
|
1551
|
+
(e) => {
|
|
1552
|
+
back.push(e);
|
|
1553
|
+
},
|
|
1554
|
+
{ stream: s, stream_exact: true }
|
|
1555
|
+
);
|
|
1556
|
+
expect3(back).toHaveLength(3);
|
|
1557
|
+
expect3(
|
|
1558
|
+
back.map((e) => ({
|
|
1559
|
+
stream: e.stream,
|
|
1560
|
+
version: e.version,
|
|
1561
|
+
name: e.name,
|
|
1562
|
+
created: e.created.toISOString(),
|
|
1563
|
+
data: e.data
|
|
1564
|
+
}))
|
|
1565
|
+
).toEqual([
|
|
1566
|
+
{
|
|
1567
|
+
stream: s,
|
|
1568
|
+
version: 0,
|
|
1569
|
+
name: "Incremented",
|
|
1570
|
+
created: t0.toISOString(),
|
|
1571
|
+
data: { amount: 1 }
|
|
1572
|
+
},
|
|
1573
|
+
{
|
|
1574
|
+
stream: s,
|
|
1575
|
+
version: 1,
|
|
1576
|
+
name: "Incremented",
|
|
1577
|
+
created: t1.toISOString(),
|
|
1578
|
+
data: { amount: 2 }
|
|
1579
|
+
},
|
|
1580
|
+
{
|
|
1581
|
+
stream: s,
|
|
1582
|
+
version: 2,
|
|
1583
|
+
name: "Decremented",
|
|
1584
|
+
created: t2.toISOString(),
|
|
1585
|
+
data: { amount: 1 }
|
|
1586
|
+
}
|
|
1587
|
+
]);
|
|
1588
|
+
});
|
|
1589
|
+
it3("rebuilds multiple streams interleaved", async () => {
|
|
1590
|
+
const a = `restore-multi-a-${uid()}`;
|
|
1591
|
+
const b = `restore-multi-b-${uid()}`;
|
|
1592
|
+
const t = /* @__PURE__ */ new Date("2020-06-01T00:00:00.000Z");
|
|
1593
|
+
const rows = [
|
|
1594
|
+
row(1, a, 0, "Incremented", t, { amount: 10 }),
|
|
1595
|
+
row(2, b, 0, "Incremented", t, { amount: 20 }),
|
|
1596
|
+
row(3, a, 1, "Decremented", t, { amount: 5 }),
|
|
1597
|
+
row(4, b, 1, "Incremented", t, { amount: 30 })
|
|
1598
|
+
];
|
|
1599
|
+
const result = await store.restore(asSource(rows));
|
|
1600
|
+
expect3(result.kept).toBe(4);
|
|
1601
|
+
const aBack = [];
|
|
1602
|
+
const bBack = [];
|
|
1603
|
+
await store.query(
|
|
1604
|
+
(e) => {
|
|
1605
|
+
aBack.push(e);
|
|
1606
|
+
},
|
|
1607
|
+
{ stream: a, stream_exact: true }
|
|
1608
|
+
);
|
|
1609
|
+
await store.query(
|
|
1610
|
+
(e) => {
|
|
1611
|
+
bBack.push(e);
|
|
1612
|
+
},
|
|
1613
|
+
{ stream: b, stream_exact: true }
|
|
1614
|
+
);
|
|
1615
|
+
expect3(aBack.map((e) => e.version)).toEqual([0, 1]);
|
|
1616
|
+
expect3(bBack.map((e) => e.version)).toEqual([0, 1]);
|
|
1617
|
+
});
|
|
1618
|
+
it3("accepts ISO string `created` and parses to Date", async () => {
|
|
1619
|
+
const s = `restore-isoc-${uid()}`;
|
|
1620
|
+
const iso = "2021-07-15T12:34:56.789Z";
|
|
1621
|
+
await store.restore(
|
|
1622
|
+
asSource([
|
|
1623
|
+
{
|
|
1624
|
+
id: 1,
|
|
1625
|
+
stream: s,
|
|
1626
|
+
version: 0,
|
|
1627
|
+
name: "Incremented",
|
|
1628
|
+
data: { amount: 1 },
|
|
1629
|
+
created: iso,
|
|
1630
|
+
meta: { correlation: "restore-tck", causation: {} }
|
|
1631
|
+
}
|
|
1632
|
+
])
|
|
1633
|
+
);
|
|
1634
|
+
const back = [];
|
|
1635
|
+
await store.query(
|
|
1636
|
+
(e) => {
|
|
1637
|
+
back.push(e);
|
|
1638
|
+
},
|
|
1639
|
+
{ stream: s, stream_exact: true }
|
|
1640
|
+
);
|
|
1641
|
+
expect3(back).toHaveLength(1);
|
|
1642
|
+
expect3(back[0].created.toISOString()).toBe(iso);
|
|
1643
|
+
});
|
|
1644
|
+
it3("wipes pre-existing events before inserting", async () => {
|
|
1645
|
+
const old = `restore-old-${uid()}`;
|
|
1646
|
+
await store.commit(
|
|
1647
|
+
old,
|
|
1648
|
+
[inc(1), inc(2)],
|
|
1649
|
+
makeMeta({ stream: old })
|
|
1650
|
+
);
|
|
1651
|
+
const fresh = `restore-fresh-${uid()}`;
|
|
1652
|
+
const t = /* @__PURE__ */ new Date("2020-01-01T00:00:00.000Z");
|
|
1653
|
+
await store.restore(
|
|
1654
|
+
asSource([row(1, fresh, 0, "Incremented", t, { amount: 99 })])
|
|
1655
|
+
);
|
|
1656
|
+
const oldBack = await collect(store, {
|
|
1657
|
+
stream: old,
|
|
1658
|
+
stream_exact: true
|
|
1659
|
+
});
|
|
1660
|
+
expect3(oldBack).toHaveLength(0);
|
|
1661
|
+
const freshBack = await collect(store, {
|
|
1662
|
+
stream: fresh,
|
|
1663
|
+
stream_exact: true
|
|
1664
|
+
});
|
|
1665
|
+
expect3(freshBack).toHaveLength(1);
|
|
1666
|
+
});
|
|
1667
|
+
it3("clears subscription/stream-position metadata", async () => {
|
|
1668
|
+
const sub = `restore-sub-${uid()}`;
|
|
1669
|
+
await store.subscribe([{ stream: sub, source: "anything" }]);
|
|
1670
|
+
const collectStreams = async () => {
|
|
1671
|
+
const out = [];
|
|
1672
|
+
await store.query_streams((p) => {
|
|
1673
|
+
out.push(p.stream);
|
|
1674
|
+
});
|
|
1675
|
+
return out;
|
|
1676
|
+
};
|
|
1677
|
+
const before = await collectStreams();
|
|
1678
|
+
expect3(before.includes(sub)).toBe(true);
|
|
1679
|
+
await store.restore(asSource([]));
|
|
1680
|
+
const after = await collectStreams();
|
|
1681
|
+
expect3(after.includes(sub)).toBe(false);
|
|
1682
|
+
});
|
|
1683
|
+
it3("preserves snapshot events through restore", async () => {
|
|
1684
|
+
const s = `restore-snap-${uid()}`;
|
|
1685
|
+
const t = /* @__PURE__ */ new Date("2020-04-01T00:00:00.000Z");
|
|
1686
|
+
await store.restore(
|
|
1687
|
+
asSource([
|
|
1688
|
+
{
|
|
1689
|
+
id: 1,
|
|
1690
|
+
stream: s,
|
|
1691
|
+
version: 0,
|
|
1692
|
+
name: SNAP_EVENT,
|
|
1693
|
+
data: { count: 42 },
|
|
1694
|
+
created: t,
|
|
1695
|
+
meta: { correlation: "snap", causation: {} }
|
|
1696
|
+
}
|
|
1697
|
+
])
|
|
1698
|
+
);
|
|
1699
|
+
const back = await collect(store, {
|
|
1700
|
+
stream: s,
|
|
1701
|
+
stream_exact: true,
|
|
1702
|
+
with_snaps: true
|
|
1703
|
+
});
|
|
1704
|
+
expect3(back).toHaveLength(1);
|
|
1705
|
+
expect3(back[0].name).toBe(SNAP_EVENT);
|
|
1706
|
+
});
|
|
1707
|
+
it3("rewrites causation refs through the old\u2192new id map", async () => {
|
|
1708
|
+
const s = `restore-caus-${uid()}`;
|
|
1709
|
+
const t = /* @__PURE__ */ new Date("2020-08-01T00:00:00.000Z");
|
|
1710
|
+
const rows = [
|
|
1711
|
+
{
|
|
1712
|
+
id: 5,
|
|
1713
|
+
stream: s,
|
|
1714
|
+
version: 0,
|
|
1715
|
+
name: "Incremented",
|
|
1716
|
+
data: { amount: 1 },
|
|
1717
|
+
created: t,
|
|
1718
|
+
meta: { correlation: "c", causation: {} }
|
|
1719
|
+
},
|
|
1720
|
+
{
|
|
1721
|
+
id: 7,
|
|
1722
|
+
stream: s,
|
|
1723
|
+
version: 1,
|
|
1724
|
+
name: "Incremented",
|
|
1725
|
+
data: { amount: 2 },
|
|
1726
|
+
created: t,
|
|
1727
|
+
meta: {
|
|
1728
|
+
correlation: "c",
|
|
1729
|
+
causation: {
|
|
1730
|
+
event: { id: 5, name: "Incremented", stream: s }
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
},
|
|
1734
|
+
{
|
|
1735
|
+
id: 9,
|
|
1736
|
+
stream: s,
|
|
1737
|
+
version: 2,
|
|
1738
|
+
name: "Decremented",
|
|
1739
|
+
data: { amount: 1 },
|
|
1740
|
+
created: t,
|
|
1741
|
+
meta: {
|
|
1742
|
+
correlation: "c",
|
|
1743
|
+
causation: {
|
|
1744
|
+
event: { id: 7, name: "Incremented", stream: s }
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
];
|
|
1749
|
+
await store.restore(asSource(rows));
|
|
1750
|
+
const back = [];
|
|
1751
|
+
await store.query(
|
|
1752
|
+
(e) => {
|
|
1753
|
+
back.push(e);
|
|
1754
|
+
},
|
|
1755
|
+
{ stream: s, stream_exact: true }
|
|
1756
|
+
);
|
|
1757
|
+
expect3(back).toHaveLength(3);
|
|
1758
|
+
expect3(back[0].meta.causation.event).toBeUndefined();
|
|
1759
|
+
expect3(back[1].meta.causation.event?.id).toBe(back[0].id);
|
|
1760
|
+
expect3(back[2].meta.causation.event?.id).toBe(back[1].id);
|
|
1761
|
+
});
|
|
1762
|
+
it3("leaves causation refs unmapped when the target isn't in the source", async () => {
|
|
1763
|
+
const s = `restore-orphan-${uid()}`;
|
|
1764
|
+
const t = /* @__PURE__ */ new Date("2020-09-01T00:00:00.000Z");
|
|
1765
|
+
await store.restore(
|
|
1766
|
+
asSource([
|
|
1767
|
+
{
|
|
1768
|
+
id: 1,
|
|
1769
|
+
stream: s,
|
|
1770
|
+
version: 0,
|
|
1771
|
+
name: "Incremented",
|
|
1772
|
+
data: { amount: 1 },
|
|
1773
|
+
created: t,
|
|
1774
|
+
meta: {
|
|
1775
|
+
correlation: "c",
|
|
1776
|
+
causation: {
|
|
1777
|
+
event: { id: 999, name: "Phantom", stream: "ghost" }
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
])
|
|
1782
|
+
);
|
|
1783
|
+
const back = [];
|
|
1784
|
+
await store.query(
|
|
1785
|
+
(e) => {
|
|
1786
|
+
back.push(e);
|
|
1787
|
+
},
|
|
1788
|
+
{ stream: s, stream_exact: true }
|
|
1789
|
+
);
|
|
1790
|
+
expect3(back[0].meta.causation.event?.id).toBe(999);
|
|
1791
|
+
});
|
|
1792
|
+
it3("rolls back atomically when the source throws mid-iteration", async () => {
|
|
1793
|
+
const original = `restore-pre-${uid()}`;
|
|
1794
|
+
const committed = await store.commit(
|
|
1795
|
+
original,
|
|
1796
|
+
[inc(1), inc(2), inc(3)],
|
|
1797
|
+
makeMeta({ stream: original })
|
|
1798
|
+
);
|
|
1799
|
+
const explosive = (async function* () {
|
|
1800
|
+
yield row(
|
|
1801
|
+
1,
|
|
1802
|
+
`restore-explode-${uid()}`,
|
|
1803
|
+
0,
|
|
1804
|
+
"Incremented",
|
|
1805
|
+
/* @__PURE__ */ new Date(),
|
|
1806
|
+
{ amount: 1 }
|
|
1807
|
+
);
|
|
1808
|
+
throw new Error("boom");
|
|
1809
|
+
})();
|
|
1810
|
+
await expect3(store.restore(explosive)).rejects.toThrow("boom");
|
|
1811
|
+
const back = [];
|
|
1812
|
+
await store.query(
|
|
1813
|
+
(e) => {
|
|
1814
|
+
back.push(e);
|
|
1815
|
+
},
|
|
1816
|
+
{ stream: original, stream_exact: true }
|
|
1817
|
+
);
|
|
1818
|
+
expect3(back).toHaveLength(3);
|
|
1819
|
+
expect3(back.map((e) => e.id)).toEqual(committed.map((c) => c.id));
|
|
1820
|
+
});
|
|
1821
|
+
});
|
|
1507
1822
|
if (caps.notify) {
|
|
1508
1823
|
describe3("notify (capability)", () => {
|
|
1509
1824
|
it3("delivers a notification when a different instance commits", async () => {
|