@reventlessdev/reventless-gwt 1.0.0-alpha.115 → 1.0.0-alpha.116

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.
Files changed (91) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/package.json +5 -12
  3. package/rescript.json +0 -1
  4. package/bin/reventless-dev.mjs +0 -21
  5. package/src/Bind.res +0 -29
  6. package/src/Bind.res.mjs +0 -22
  7. package/src/BuildClassifier.res +0 -147
  8. package/src/BuildClassifier.res.mjs +0 -136
  9. package/src/Cancellation.res +0 -37
  10. package/src/Cancellation.res.mjs +0 -46
  11. package/src/ChildProcess.res +0 -102
  12. package/src/ChildProcess.res.mjs +0 -107
  13. package/src/Cli.res +0 -889
  14. package/src/Cli.res.mjs +0 -944
  15. package/src/Collector.res +0 -222
  16. package/src/Collector.res.mjs +0 -228
  17. package/src/ComponentMeta.res +0 -77
  18. package/src/ComponentMeta.res.mjs +0 -91
  19. package/src/ComponentScan.res +0 -101
  20. package/src/ComponentScan.res.mjs +0 -92
  21. package/src/Discovery.res +0 -102
  22. package/src/Discovery.res.mjs +0 -86
  23. package/src/DomainDeadCode.res +0 -98
  24. package/src/DomainDeadCode.res.mjs +0 -72
  25. package/src/DomainGraph.res +0 -0
  26. package/src/DomainGraph.res.mjs +0 -0
  27. package/src/ExnMessage.res +0 -14
  28. package/src/ExnMessage.res.mjs +0 -17
  29. package/src/ExnReflect.mjs +0 -24
  30. package/src/Filter.res +0 -45
  31. package/src/Filter.res.mjs +0 -47
  32. package/src/FormatterHuman.res +0 -109
  33. package/src/FormatterHuman.res.mjs +0 -115
  34. package/src/FormatterJson.res +0 -264
  35. package/src/FormatterJson.res.mjs +0 -322
  36. package/src/FormatterJunit.res +0 -93
  37. package/src/FormatterJunit.res.mjs +0 -90
  38. package/src/FormatterTap.res +0 -100
  39. package/src/FormatterTap.res.mjs +0 -131
  40. package/src/FormatterVsCode.res +0 -352
  41. package/src/FormatterVsCode.res.mjs +0 -546
  42. package/src/Loader.res +0 -24
  43. package/src/Loader.res.mjs +0 -23
  44. package/src/PackageScan.res +0 -80
  45. package/src/PackageScan.res.mjs +0 -81
  46. package/src/PlatformRunner.res +0 -191
  47. package/src/PlatformRunner.res.mjs +0 -178
  48. package/src/PlatformScan.res +0 -141
  49. package/src/PlatformScan.res.mjs +0 -124
  50. package/src/ProcessManager.res +0 -135
  51. package/src/ProcessManager.res.mjs +0 -119
  52. package/src/RunWorker.res +0 -24
  53. package/src/RunWorker.res.mjs +0 -16
  54. package/src/RunnerTypes.res +0 -68
  55. package/src/RunnerTypes.res.mjs +0 -60
  56. package/src/ScanIgnore.res +0 -11
  57. package/src/ScanIgnore.res.mjs +0 -23
  58. package/src/Watch.res +0 -102
  59. package/src/Watch.res.mjs +0 -80
  60. package/src/WatcherProbe.res +0 -56
  61. package/src/WatcherProbe.res.mjs +0 -59
  62. package/src/Worker.res +0 -24
  63. package/src/Worker.res.mjs +0 -9
  64. package/tests/BuildClassifierTest.res +0 -133
  65. package/tests/BuildClassifierTest.res.mjs +0 -176
  66. package/tests/CliRunEntryTest.res +0 -57
  67. package/tests/CliRunEntryTest.res.mjs +0 -65
  68. package/tests/CliWatchScopeTest.res +0 -62
  69. package/tests/CliWatchScopeTest.res.mjs +0 -95
  70. package/tests/ComponentMetaTest.res +0 -67
  71. package/tests/ComponentMetaTest.res.mjs +0 -71
  72. package/tests/DiscoveryTest.res +0 -42
  73. package/tests/DiscoveryTest.res.mjs +0 -43
  74. package/tests/DomainDeadCodeTest.res +0 -119
  75. package/tests/DomainDeadCodeTest.res.mjs +0 -158
  76. package/tests/DomainGraphTest.res +0 -448
  77. package/tests/DomainGraphTest.res.mjs +0 -505
  78. package/tests/ExnMessageTest.res +0 -42
  79. package/tests/ExnMessageTest.res.mjs +0 -64
  80. package/tests/FormatterGoldenTest.res +0 -125
  81. package/tests/FormatterGoldenTest.res.mjs +0 -162
  82. package/tests/FormatterVsCodeMessageTest.res +0 -41
  83. package/tests/FormatterVsCodeMessageTest.res.mjs +0 -42
  84. package/tests/PlatformRunnerTest.res +0 -66
  85. package/tests/PlatformRunnerTest.res.mjs +0 -85
  86. package/tests/PlatformScanTest.res +0 -61
  87. package/tests/PlatformScanTest.res.mjs +0 -56
  88. package/tests/RunnerUnitTest.res +0 -219
  89. package/tests/RunnerUnitTest.res.mjs +0 -244
  90. package/tests/WatchDebounceTest.res +0 -106
  91. package/tests/WatchDebounceTest.res.mjs +0 -128
@@ -1,62 +0,0 @@
1
- // Unit tests for the watch re-run scope algebra (B1): a plain edit narrows the
2
- // re-run to its owning test package, structural/add/full triggers widen to
3
- // RunAll, and scopes coalesce (RunAll absorbing) while a pass is in flight.
4
-
5
- open JestGlobals
6
-
7
- describe("Cli.mergeScope", () => {
8
- testSync("RunAll absorbs on the left", () =>
9
- expect(Cli.mergeScope(RunAll, RunPackages(["/p/a"])))->toEqual(Cli.RunAll)
10
- )
11
- testSync("RunAll absorbs on the right", () =>
12
- expect(Cli.mergeScope(RunPackages(["/p/a"]), RunAll))->toEqual(Cli.RunAll)
13
- )
14
- testSync("two RunPackages union their dirs, deduplicated", () =>
15
- expect(Cli.mergeScope(RunPackages(["/p/a", "/p/b"]), RunPackages(["/p/b", "/p/c"])))->toEqual(
16
- Cli.RunPackages(["/p/a", "/p/b", "/p/c"]),
17
- )
18
- )
19
- testSync("RunAll merged with RunAll stays RunAll", () =>
20
- expect(Cli.mergeScope(RunAll, RunAll))->toEqual(Cli.RunAll)
21
- )
22
- })
23
-
24
- describe("Cli.pathUnderDir", () => {
25
- testSync("a file inside the dir is under it", () =>
26
- expect(Cli.pathUnderDir("/p/pkgA/tests/FooGwt.res.mjs", "/p/pkgA"))->toEqual(true)
27
- )
28
- testSync("a sibling with a shared prefix is NOT under it", () =>
29
- // /p/pkgABC must not be treated as inside /p/pkgA — the trailing separator
30
- // guards against the bare-prefix false positive.
31
- expect(Cli.pathUnderDir("/p/pkgABC/tests/FooGwt.res.mjs", "/p/pkgA"))->toEqual(false)
32
- )
33
- testSync("an unrelated path is not under it", () =>
34
- expect(Cli.pathUnderDir("/p/pkgB/tests/BarGwt.res.mjs", "/p/pkgA"))->toEqual(false)
35
- )
36
- testSync("a trailing-slash dir still matches", () =>
37
- expect(Cli.pathUnderDir("/p/pkgA/tests/FooGwt.res.mjs", "/p/pkgA/"))->toEqual(true)
38
- )
39
- })
40
-
41
- describe("Cli.scopeSubset", () => {
42
- let all = [
43
- "/p/pkgA/tests/FooGwt.res.mjs",
44
- "/p/pkgA/tests/BarGwt.res.mjs",
45
- "/p/pkgB/tests/BazGwt.res.mjs",
46
- ]
47
- testSync("RunAll returns every path", () =>
48
- expect(Cli.scopeSubset(RunAll, all))->toEqual(all)
49
- )
50
- testSync("RunPackages keeps only files under the given dirs", () =>
51
- expect(Cli.scopeSubset(RunPackages(["/p/pkgA"]), all))->toEqual([
52
- "/p/pkgA/tests/FooGwt.res.mjs",
53
- "/p/pkgA/tests/BarGwt.res.mjs",
54
- ])
55
- )
56
- testSync("RunPackages over multiple dirs unions their files", () =>
57
- expect(Cli.scopeSubset(RunPackages(["/p/pkgA", "/p/pkgB"]), all))->toEqual(all)
58
- )
59
- testSync("RunPackages with no matching dir yields no files", () =>
60
- expect(Cli.scopeSubset(RunPackages(["/p/pkgZ"]), all))->toEqual([])
61
- )
62
- })
@@ -1,95 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Cli$ReventlessGwt from "../src/Cli.res.mjs";
4
-
5
- globalThis.describe("Cli.mergeScope", () => {
6
- globalThis.test("RunAll absorbs on the left", () => {
7
- globalThis.expect(Cli$ReventlessGwt.mergeScope("RunAll", {
8
- TAG: "RunPackages",
9
- _0: ["/p/a"]
10
- })).toEqual("RunAll");
11
- });
12
- globalThis.test("RunAll absorbs on the right", () => {
13
- globalThis.expect(Cli$ReventlessGwt.mergeScope({
14
- TAG: "RunPackages",
15
- _0: ["/p/a"]
16
- }, "RunAll")).toEqual("RunAll");
17
- });
18
- globalThis.test("two RunPackages union their dirs, deduplicated", () => {
19
- globalThis.expect(Cli$ReventlessGwt.mergeScope({
20
- TAG: "RunPackages",
21
- _0: [
22
- "/p/a",
23
- "/p/b"
24
- ]
25
- }, {
26
- TAG: "RunPackages",
27
- _0: [
28
- "/p/b",
29
- "/p/c"
30
- ]
31
- })).toEqual({
32
- TAG: "RunPackages",
33
- _0: [
34
- "/p/a",
35
- "/p/b",
36
- "/p/c"
37
- ]
38
- });
39
- });
40
- globalThis.test("RunAll merged with RunAll stays RunAll", () => {
41
- globalThis.expect(Cli$ReventlessGwt.mergeScope("RunAll", "RunAll")).toEqual("RunAll");
42
- });
43
- });
44
-
45
- globalThis.describe("Cli.pathUnderDir", () => {
46
- globalThis.test("a file inside the dir is under it", () => {
47
- globalThis.expect(Cli$ReventlessGwt.pathUnderDir("/p/pkgA/tests/FooGwt.res.mjs", "/p/pkgA")).toEqual(true);
48
- });
49
- globalThis.test("a sibling with a shared prefix is NOT under it", () => {
50
- globalThis.expect(Cli$ReventlessGwt.pathUnderDir("/p/pkgABC/tests/FooGwt.res.mjs", "/p/pkgA")).toEqual(false);
51
- });
52
- globalThis.test("an unrelated path is not under it", () => {
53
- globalThis.expect(Cli$ReventlessGwt.pathUnderDir("/p/pkgB/tests/BarGwt.res.mjs", "/p/pkgA")).toEqual(false);
54
- });
55
- globalThis.test("a trailing-slash dir still matches", () => {
56
- globalThis.expect(Cli$ReventlessGwt.pathUnderDir("/p/pkgA/tests/FooGwt.res.mjs", "/p/pkgA/")).toEqual(true);
57
- });
58
- });
59
-
60
- globalThis.describe("Cli.scopeSubset", () => {
61
- let all = [
62
- "/p/pkgA/tests/FooGwt.res.mjs",
63
- "/p/pkgA/tests/BarGwt.res.mjs",
64
- "/p/pkgB/tests/BazGwt.res.mjs"
65
- ];
66
- globalThis.test("RunAll returns every path", () => {
67
- globalThis.expect(Cli$ReventlessGwt.scopeSubset("RunAll", all)).toEqual(all);
68
- });
69
- globalThis.test("RunPackages keeps only files under the given dirs", () => {
70
- globalThis.expect(Cli$ReventlessGwt.scopeSubset({
71
- TAG: "RunPackages",
72
- _0: ["/p/pkgA"]
73
- }, all)).toEqual([
74
- "/p/pkgA/tests/FooGwt.res.mjs",
75
- "/p/pkgA/tests/BarGwt.res.mjs"
76
- ]);
77
- });
78
- globalThis.test("RunPackages over multiple dirs unions their files", () => {
79
- globalThis.expect(Cli$ReventlessGwt.scopeSubset({
80
- TAG: "RunPackages",
81
- _0: [
82
- "/p/pkgA",
83
- "/p/pkgB"
84
- ]
85
- }, all)).toEqual(all);
86
- });
87
- globalThis.test("RunPackages with no matching dir yields no files", () => {
88
- globalThis.expect(Cli$ReventlessGwt.scopeSubset({
89
- TAG: "RunPackages",
90
- _0: ["/p/pkgZ"]
91
- }, all)).toEqual([]);
92
- });
93
- });
94
-
95
- /* Not a pure module */
@@ -1,67 +0,0 @@
1
- // Unit tests for ComponentMeta — the pure folder-convention → {kind, name}
2
- // derivation behind the `component` field on file items and the `components`
3
- // inventory. No I/O; path strings only.
4
-
5
- open JestGlobals
6
-
7
- describe("ComponentMeta.componentOfTestFile", () => {
8
- testPromise("derives kind + name from a slice GWT test path", async () => {
9
- let c = ComponentMeta.componentOfTestFile(
10
- "/repo/catalog/tests/Product/StateChangeSlice/AddProduct_GWT.res.mjs",
11
- )
12
- expect(c)->toEqual(Some({ComponentMeta.kind: "StateChangeSlice", name: "AddProduct"}))
13
- })
14
-
15
- testPromise("derives a ReadModel component", async () => {
16
- let c = ComponentMeta.componentOfTestFile(
17
- "/repo/catalog/tests/CatalogActivity/ReadModel/CatalogActivity_GWT.res.mjs",
18
- )
19
- expect(c)->toEqual(Some({ComponentMeta.kind: "ReadModel", name: "CatalogActivity"}))
20
- })
21
-
22
- testPromise("strips the GwtTest marker shape too", async () => {
23
- let c = ComponentMeta.componentOfTestFile("/repo/x/tests/Order/Aggregate/OrderGwtTest.res.mjs")
24
- expect(c)->toEqual(Some({ComponentMeta.kind: "Aggregate", name: "Order"}))
25
- })
26
-
27
- testPromise("returns None outside a recognised kind folder", async () => {
28
- let c = ComponentMeta.componentOfTestFile("/repo/x/tests/Helpers/Thing_GWT.res.mjs")
29
- expect(c)->toEqual(None)
30
- })
31
-
32
- testPromise("recognises a plural folder spelling with the canonical kind (C2)", async () => {
33
- // The generator accepts `Aggregates/`; previously the gwt discovery only knew
34
- // the singular `Aggregate/` and silently returned None. Now it derives from
35
- // the shared vocabulary and reports the canonical singular kind.
36
- let c = ComponentMeta.componentOfTestFile("/repo/x/tests/Order/Aggregates/Order_GWT.res.mjs")
37
- expect(c)->toEqual(Some({ComponentMeta.kind: "Aggregate", name: "Order"}))
38
- })
39
-
40
- testPromise("recognises a short slice folder spelling (StateChange) too (C2)", async () => {
41
- let c = ComponentMeta.componentOfTestFile(
42
- "/repo/catalog/tests/Product/StateChange/AddProduct_GWT.res.mjs",
43
- )
44
- expect(c)->toEqual(Some({ComponentMeta.kind: "StateChangeSlice", name: "AddProduct"}))
45
- })
46
- })
47
-
48
- describe("ComponentMeta.componentOfSrcFile", () => {
49
- testPromise("collapses a spec file to its component", async () => {
50
- let c = ComponentMeta.componentOfSrcFile("/repo/catalog/src/Product/Aggregate/Product.res")
51
- expect(c)->toEqual(Some({ComponentMeta.kind: "Aggregate", name: "Product"}))
52
- })
53
-
54
- testPromise("collapses a behavior body file to the same component", async () => {
55
- let c = ComponentMeta.componentOfSrcFile(
56
- "/repo/catalog/src/Product/Aggregate/Product_Behavior.res",
57
- )
58
- expect(c)->toEqual(Some({ComponentMeta.kind: "Aggregate", name: "Product"}))
59
- })
60
-
61
- testPromise("strips the longest body suffix first (ExtensionPointMapping)", async () => {
62
- let c = ComponentMeta.componentOfSrcFile(
63
- "/repo/catalog/src/Products/ExtensionPoint/Products_ExtensionPointMapping.res",
64
- )
65
- expect(c)->toEqual(Some({ComponentMeta.kind: "ExtensionPoint", name: "Products"}))
66
- })
67
- })
@@ -1,71 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as ComponentMeta$ReventlessGwt from "../src/ComponentMeta.res.mjs";
4
-
5
- globalThis.describe("ComponentMeta.componentOfTestFile", () => {
6
- globalThis.test("derives kind + name from a slice GWT test path", async () => {
7
- let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/catalog/tests/Product/StateChangeSlice/AddProduct_GWT.res.mjs");
8
- globalThis.expect(c).toEqual({
9
- kind: "StateChangeSlice",
10
- name: "AddProduct"
11
- });
12
- });
13
- globalThis.test("derives a ReadModel component", async () => {
14
- let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/catalog/tests/CatalogActivity/ReadModel/CatalogActivity_GWT.res.mjs");
15
- globalThis.expect(c).toEqual({
16
- kind: "ReadModel",
17
- name: "CatalogActivity"
18
- });
19
- });
20
- globalThis.test("strips the GwtTest marker shape too", async () => {
21
- let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/x/tests/Order/Aggregate/OrderGwtTest.res.mjs");
22
- globalThis.expect(c).toEqual({
23
- kind: "Aggregate",
24
- name: "Order"
25
- });
26
- });
27
- globalThis.test("returns None outside a recognised kind folder", async () => {
28
- let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/x/tests/Helpers/Thing_GWT.res.mjs");
29
- globalThis.expect(c).toEqual(undefined);
30
- });
31
- globalThis.test("recognises a plural folder spelling with the canonical kind (C2)", async () => {
32
- let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/x/tests/Order/Aggregates/Order_GWT.res.mjs");
33
- globalThis.expect(c).toEqual({
34
- kind: "Aggregate",
35
- name: "Order"
36
- });
37
- });
38
- globalThis.test("recognises a short slice folder spelling (StateChange) too (C2)", async () => {
39
- let c = ComponentMeta$ReventlessGwt.componentOfTestFile("/repo/catalog/tests/Product/StateChange/AddProduct_GWT.res.mjs");
40
- globalThis.expect(c).toEqual({
41
- kind: "StateChangeSlice",
42
- name: "AddProduct"
43
- });
44
- });
45
- });
46
-
47
- globalThis.describe("ComponentMeta.componentOfSrcFile", () => {
48
- globalThis.test("collapses a spec file to its component", async () => {
49
- let c = ComponentMeta$ReventlessGwt.componentOfSrcFile("/repo/catalog/src/Product/Aggregate/Product.res");
50
- globalThis.expect(c).toEqual({
51
- kind: "Aggregate",
52
- name: "Product"
53
- });
54
- });
55
- globalThis.test("collapses a behavior body file to the same component", async () => {
56
- let c = ComponentMeta$ReventlessGwt.componentOfSrcFile("/repo/catalog/src/Product/Aggregate/Product_Behavior.res");
57
- globalThis.expect(c).toEqual({
58
- kind: "Aggregate",
59
- name: "Product"
60
- });
61
- });
62
- globalThis.test("strips the longest body suffix first (ExtensionPointMapping)", async () => {
63
- let c = ComponentMeta$ReventlessGwt.componentOfSrcFile("/repo/catalog/src/Products/ExtensionPoint/Products_ExtensionPointMapping.res");
64
- globalThis.expect(c).toEqual({
65
- kind: "ExtensionPoint",
66
- name: "Products"
67
- });
68
- });
69
- });
70
-
71
- /* Not a pure module */
@@ -1,42 +0,0 @@
1
- // Unit test for the `.gwtignore` subtree prune in `Discovery.discover`. Builds a
2
- // throwaway tree under the OS temp dir: a `keep/` directory with a GWT test, and
3
- // a `skip/` directory carrying a `.gwtignore` sentinel alongside its own GWT
4
- // test. Discovery must return the former and prune the latter — the guarantee
5
- // the codegen golden fixtures rely on (they ship compiled `*_GWT.res.mjs` on
6
- // disk that must never surface as live tests).
7
-
8
- open JestGlobals
9
-
10
- @module("node:os") external tmpdir: unit => string = "tmpdir"
11
- @module("node:path") external join: (string, string) => string = "join"
12
-
13
- type mkdirOpts = {recursive: bool}
14
- @module("node:fs/promises") external mkdir: (string, mkdirOpts) => promise<Nullable.t<string>> = "mkdir"
15
- @module("node:fs/promises") external writeFile: (string, string) => promise<unit> = "writeFile"
16
-
17
- type rmOpts = {recursive: bool, force: bool}
18
- @module("node:fs/promises") external rm: (string, rmOpts) => promise<unit> = "rm"
19
-
20
- let endsWith = (paths: array<string>, suffix: string) =>
21
- paths->Array.some(p => String.endsWith(p, suffix))
22
-
23
- describe("Discovery .gwtignore prune", () => {
24
- testPromise("keeps sibling GWT tests but prunes a subtree carrying .gwtignore", async () => {
25
- let root = join(tmpdir(), "reventless-gwt-gwtignore-test")
26
- let _ = await rm(root, {recursive: true, force: true})
27
- let keep = join(root, "keep")
28
- let skip = join(root, "skip")
29
- let _ = await mkdir(keep, {recursive: true})
30
- let _ = await mkdir(skip, {recursive: true})
31
- let _ = await writeFile(join(keep, "Keep_GWT.res.mjs"), "")
32
- let _ = await writeFile(join(skip, "Skip_GWT.res.mjs"), "")
33
- let _ = await writeFile(join(skip, ".gwtignore"), "")
34
-
35
- let found = await Discovery.discover([root])
36
-
37
- expect(found->endsWith("keep/Keep_GWT.res.mjs"))->toBe(true)
38
- expect(found->endsWith("skip/Skip_GWT.res.mjs"))->toBe(false)
39
-
40
- let _ = await rm(root, {recursive: true, force: true})
41
- })
42
- })
@@ -1,43 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Nodeos from "node:os";
4
- import * as Nodepath from "node:path";
5
- import * as Promises from "node:fs/promises";
6
- import * as Discovery$ReventlessGwt from "../src/Discovery.res.mjs";
7
-
8
- function endsWith(paths, suffix) {
9
- return paths.some(p => p.endsWith(suffix));
10
- }
11
-
12
- globalThis.describe("Discovery .gwtignore prune", () => {
13
- globalThis.test("keeps sibling GWT tests but prunes a subtree carrying .gwtignore", async () => {
14
- let root = Nodepath.join(Nodeos.tmpdir(), "reventless-gwt-gwtignore-test");
15
- await Promises.rm(root, {
16
- recursive: true,
17
- force: true
18
- });
19
- let keep = Nodepath.join(root, "keep");
20
- let skip = Nodepath.join(root, "skip");
21
- await Promises.mkdir(keep, {
22
- recursive: true
23
- });
24
- await Promises.mkdir(skip, {
25
- recursive: true
26
- });
27
- await Promises.writeFile(Nodepath.join(keep, "Keep_GWT.res.mjs"), "");
28
- await Promises.writeFile(Nodepath.join(skip, "Skip_GWT.res.mjs"), "");
29
- await Promises.writeFile(Nodepath.join(skip, ".gwtignore"), "");
30
- let found = await Discovery$ReventlessGwt.discover([root]);
31
- globalThis.expect(endsWith(found, "keep/Keep_GWT.res.mjs")).toBe(true);
32
- globalThis.expect(endsWith(found, "skip/Skip_GWT.res.mjs")).toBe(false);
33
- await Promises.rm(root, {
34
- recursive: true,
35
- force: true
36
- });
37
- });
38
- });
39
-
40
- export {
41
- endsWith,
42
- }
43
- /* Not a pure module */
@@ -1,119 +0,0 @@
1
- // Unit tests for DomainDeadCode.analyze — orphan-event reachability over synthetic
2
- // pluginStructures. Pure (no reventless-local import), so it runs under Jest. The
3
- // real end-to-end pass over the example plugins is exercised by the LocalHost
4
- // integration test; here we drive the analysis with hand-built fixtures to pin the
5
- // orphan / not-orphan boundary precisely.
6
-
7
- open JestGlobals
8
-
9
- // ── terse builders for the verbose pluginStructure sub-records ──
10
- let writable = (~name, ~produces=[], ~consumes=[], ~linkedViews=[]): Reventless.Plugin.writableDef => {
11
- name,
12
- commands: [],
13
- producedEventTypes: produces,
14
- consumedEventTypes: consumes,
15
- linkedViews,
16
- consistencyRead: None,
17
- events: [],
18
- chapter: None,
19
- }
20
-
21
- let queryable = (~name, ~consumes=[]): Reventless.Plugin.queryableDef => {
22
- name,
23
- queryField: "",
24
- schema: "",
25
- consumedEventTypes: consumes,
26
- linkedWriteSide: [],
27
- labelField: "id",
28
- searchableFields: [],
29
- statusField: None,
30
- visibility: None,
31
- chapter: None,
32
- }
33
-
34
- let automation = (~name, ~consumes=[]): Reventless.Plugin.automationSliceDef => {
35
- name,
36
- consumedEventTypes: consumes,
37
- producedCommandTypes: [],
38
- targetName: "",
39
- chapter: None,
40
- }
41
-
42
- let structure = (
43
- ~aggregates=[],
44
- ~readModels=[],
45
- ~stateViewSlices=[],
46
- ~stateChangeSlices=[],
47
- ~automationSlices=[],
48
- ~outboundTranslationSlices=[],
49
- ~extensions=[],
50
- ): Reventless.Plugin.pluginStructure => {
51
- readModels,
52
- stateViewSlices,
53
- stateChangeSlices,
54
- aggregates,
55
- automationSlices,
56
- outboundTranslationSlices,
57
- inboundTranslationSlices: [],
58
- extensions,
59
- extensionPoints: None,
60
- }
61
-
62
- describe("DomainDeadCode.analyze — orphan events", () => {
63
- testPromise("flags a produced event no component consumes", async () => {
64
- let shop = structure(
65
- ~aggregates=[writable(~name="Order", ~produces=["Shop.Placed", "Shop.Archived"])],
66
- ~readModels=[queryable(~name="Orders", ~consumes=["Shop.Placed"])],
67
- )
68
- let findings = DomainDeadCode.analyze(~structures=[("Shop", shop)])
69
- expect(findings->Array.map(f => f.detail))->toEqual(["Shop.Archived"])
70
- let f = findings->Array.getUnsafe(0)
71
- expect((f.kind, f.pluginName, f.componentName))->toEqual(("OrphanEvent", "Shop", "Order"))
72
- })
73
-
74
- testPromise("a consumer in ANY plugin clears the orphan (cross-plugin)", async () => {
75
- let shop = structure(~aggregates=[writable(~name="Order", ~produces=["Shop.Placed"])])
76
- let analytics = structure(~automationSlices=[automation(~name="OnPlaced", ~consumes=["Shop.Placed"])])
77
- let findings = DomainDeadCode.analyze(
78
- ~structures=[("Shop", shop), ("Analytics", analytics)]
79
- )
80
- expect(findings->Array.length)->toBe(0)
81
- })
82
-
83
- testPromise("consumption via a state-view slice and a state-change DCB read both count", async () => {
84
- let s = structure(
85
- ~aggregates=[writable(~name="Order", ~produces=["Shop.Placed", "Shop.Shipped"])],
86
- ~stateViewSlices=[queryable(~name="OrderView", ~consumes=["Shop.Placed"])],
87
- ~stateChangeSlices=[writable(~name="Ship", ~consumes=["Shop.Shipped"])],
88
- )
89
- let findings = DomainDeadCode.analyze(~structures=[("Shop", s)])
90
- expect(findings->Array.length)->toBe(0)
91
- })
92
-
93
- testPromise("a linked classic read model clears all the aggregate's events (rule 2)", async () => {
94
- // Classic aggregate → read-model link lives on the producer (`linkedViews`); the
95
- // read model's consumedEventTypes is empty. The aggregate must not be flagged.
96
- let shop = structure(
97
- ~aggregates=[
98
- writable(~name="Order", ~produces=["Shop.Placed", "Shop.Archived"], ~linkedViews=["Orders"]),
99
- ],
100
- ~readModels=[queryable(~name="Orders")],
101
- )
102
- let findings = DomainDeadCode.analyze(~structures=[("Shop", shop)])
103
- expect(findings->Array.length)->toBe(0)
104
- })
105
-
106
- testPromise("an aggregate with no view and no consumer is fully orphaned", async () => {
107
- let shop = structure(~aggregates=[writable(~name="Audit", ~produces=["Shop.Logged"])])
108
- let findings = DomainDeadCode.analyze(~structures=[("Shop", shop)])
109
- expect(findings->Array.map(f => f.detail))->toEqual(["Shop.Logged"])
110
- })
111
-
112
- testPromise("state-change slices are scanned as producers too", async () => {
113
- let s = structure(
114
- ~stateChangeSlices=[writable(~name="Reserve", ~produces=["Shop.Reserved"])],
115
- )
116
- let findings = DomainDeadCode.analyze(~structures=[("Shop", s)])
117
- expect(findings->Array.map(f => (f.componentName, f.detail)))->toEqual([("Reserve", "Shop.Reserved")])
118
- })
119
- })
@@ -1,158 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as DomainDeadCode$ReventlessGwt from "../src/DomainDeadCode.res.mjs";
4
-
5
- function writable(name, producesOpt, consumesOpt, linkedViewsOpt) {
6
- let produces = producesOpt !== undefined ? producesOpt : [];
7
- let consumes = consumesOpt !== undefined ? consumesOpt : [];
8
- let linkedViews = linkedViewsOpt !== undefined ? linkedViewsOpt : [];
9
- return {
10
- name: name,
11
- commands: [],
12
- producedEventTypes: produces,
13
- consumedEventTypes: consumes,
14
- linkedViews: linkedViews,
15
- consistencyRead: undefined,
16
- events: [],
17
- chapter: undefined
18
- };
19
- }
20
-
21
- function queryable(name, consumesOpt) {
22
- let consumes = consumesOpt !== undefined ? consumesOpt : [];
23
- return {
24
- name: name,
25
- queryField: "",
26
- schema: "",
27
- consumedEventTypes: consumes,
28
- linkedWriteSide: [],
29
- labelField: "id",
30
- searchableFields: [],
31
- statusField: undefined,
32
- visibility: undefined,
33
- chapter: undefined
34
- };
35
- }
36
-
37
- function automation(name, consumesOpt) {
38
- let consumes = consumesOpt !== undefined ? consumesOpt : [];
39
- return {
40
- name: name,
41
- consumedEventTypes: consumes,
42
- producedCommandTypes: [],
43
- targetName: "",
44
- chapter: undefined
45
- };
46
- }
47
-
48
- function structure(aggregatesOpt, readModelsOpt, stateViewSlicesOpt, stateChangeSlicesOpt, automationSlicesOpt, outboundTranslationSlicesOpt, extensionsOpt) {
49
- let aggregates = aggregatesOpt !== undefined ? aggregatesOpt : [];
50
- let readModels = readModelsOpt !== undefined ? readModelsOpt : [];
51
- let stateViewSlices = stateViewSlicesOpt !== undefined ? stateViewSlicesOpt : [];
52
- let stateChangeSlices = stateChangeSlicesOpt !== undefined ? stateChangeSlicesOpt : [];
53
- let automationSlices = automationSlicesOpt !== undefined ? automationSlicesOpt : [];
54
- let outboundTranslationSlices = outboundTranslationSlicesOpt !== undefined ? outboundTranslationSlicesOpt : [];
55
- let extensions = extensionsOpt !== undefined ? extensionsOpt : [];
56
- return {
57
- readModels: readModels,
58
- stateViewSlices: stateViewSlices,
59
- stateChangeSlices: stateChangeSlices,
60
- aggregates: aggregates,
61
- automationSlices: automationSlices,
62
- outboundTranslationSlices: outboundTranslationSlices,
63
- inboundTranslationSlices: [],
64
- extensions: extensions,
65
- extensionPoints: undefined
66
- };
67
- }
68
-
69
- globalThis.describe("DomainDeadCode.analyze — orphan events", () => {
70
- globalThis.test("flags a produced event no component consumes", async () => {
71
- let shop = structure([writable("Order", [
72
- "Shop.Placed",
73
- "Shop.Archived"
74
- ], undefined, undefined)], [queryable("Orders", ["Shop.Placed"])], undefined, undefined, undefined, undefined, undefined);
75
- let findings = DomainDeadCode$ReventlessGwt.analyze([[
76
- "Shop",
77
- shop
78
- ]]);
79
- globalThis.expect(findings.map(f => f.detail)).toEqual(["Shop.Archived"]);
80
- let f = findings[0];
81
- globalThis.expect([
82
- f.kind,
83
- f.pluginName,
84
- f.componentName
85
- ]).toEqual([
86
- "OrphanEvent",
87
- "Shop",
88
- "Order"
89
- ]);
90
- });
91
- globalThis.test("a consumer in ANY plugin clears the orphan (cross-plugin)", async () => {
92
- let shop = structure([writable("Order", ["Shop.Placed"], undefined, undefined)], undefined, undefined, undefined, undefined, undefined, undefined);
93
- let analytics = structure(undefined, undefined, undefined, undefined, [automation("OnPlaced", ["Shop.Placed"])], undefined, undefined);
94
- let findings = DomainDeadCode$ReventlessGwt.analyze([
95
- [
96
- "Shop",
97
- shop
98
- ],
99
- [
100
- "Analytics",
101
- analytics
102
- ]
103
- ]);
104
- globalThis.expect(findings.length).toBe(0);
105
- });
106
- globalThis.test("consumption via a state-view slice and a state-change DCB read both count", async () => {
107
- let s = structure([writable("Order", [
108
- "Shop.Placed",
109
- "Shop.Shipped"
110
- ], undefined, undefined)], undefined, [queryable("OrderView", ["Shop.Placed"])], [writable("Ship", undefined, ["Shop.Shipped"], undefined)], undefined, undefined, undefined);
111
- let findings = DomainDeadCode$ReventlessGwt.analyze([[
112
- "Shop",
113
- s
114
- ]]);
115
- globalThis.expect(findings.length).toBe(0);
116
- });
117
- globalThis.test("a linked classic read model clears all the aggregate's events (rule 2)", async () => {
118
- let shop = structure([writable("Order", [
119
- "Shop.Placed",
120
- "Shop.Archived"
121
- ], undefined, ["Orders"])], [queryable("Orders", undefined)], undefined, undefined, undefined, undefined, undefined);
122
- let findings = DomainDeadCode$ReventlessGwt.analyze([[
123
- "Shop",
124
- shop
125
- ]]);
126
- globalThis.expect(findings.length).toBe(0);
127
- });
128
- globalThis.test("an aggregate with no view and no consumer is fully orphaned", async () => {
129
- let shop = structure([writable("Audit", ["Shop.Logged"], undefined, undefined)], undefined, undefined, undefined, undefined, undefined, undefined);
130
- let findings = DomainDeadCode$ReventlessGwt.analyze([[
131
- "Shop",
132
- shop
133
- ]]);
134
- globalThis.expect(findings.map(f => f.detail)).toEqual(["Shop.Logged"]);
135
- });
136
- globalThis.test("state-change slices are scanned as producers too", async () => {
137
- let s = structure(undefined, undefined, undefined, [writable("Reserve", ["Shop.Reserved"], undefined, undefined)], undefined, undefined, undefined);
138
- let findings = DomainDeadCode$ReventlessGwt.analyze([[
139
- "Shop",
140
- s
141
- ]]);
142
- globalThis.expect(findings.map(f => [
143
- f.componentName,
144
- f.detail
145
- ])).toEqual([[
146
- "Reserve",
147
- "Shop.Reserved"
148
- ]]);
149
- });
150
- });
151
-
152
- export {
153
- writable,
154
- queryable,
155
- automation,
156
- structure,
157
- }
158
- /* Not a pure module */