@reventlessdev/reventless-local 3.0.0-alpha.311 → 3.0.0-alpha.313
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/CHANGELOG.md +14 -0
- package/package.json +10 -10
- package/src/components/AutomationSlice_Builder.res +1 -0
- package/src/components/AutomationSlice_Builder.res.mjs +3 -0
- package/tests/components/automationslice/AutomationSliceCallbackTest.res +45 -22
- package/tests/components/automationslice/AutomationSliceCallbackTest.res.mjs +34 -33
- package/tests/components/automationslice/AutomationSlicePiiTest.res +178 -0
- package/tests/components/automationslice/AutomationSlicePiiTest.res.mjs +352 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res +82 -33
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res.mjs +38 -38
- package/tests/components/outboundtranslationslice/OutboundTranslationSlicePiiTest.res +160 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSlicePiiTest.res.mjs +300 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
// An outbound work item that carries personal data is stored as an event is: its
|
|
2
|
+
// marked fields sealed, its row keyed by a fingerprint of the key `collect`
|
|
3
|
+
// chose, opened before it is translated, and completed without a call once its
|
|
4
|
+
// subject is erased.
|
|
5
|
+
|
|
6
|
+
open JestGlobals
|
|
7
|
+
|
|
8
|
+
let sent: ref<array<string>> = ref([])
|
|
9
|
+
|
|
10
|
+
module VerifySpec = {
|
|
11
|
+
let name = "VerifyAddress"
|
|
12
|
+
let moduleUrl: string = %raw(`import.meta.url`)
|
|
13
|
+
|
|
14
|
+
@schema
|
|
15
|
+
type consumedEvent = AddressGiven({customerId: string, email: string})
|
|
16
|
+
|
|
17
|
+
@schema
|
|
18
|
+
type outboundItem = {
|
|
19
|
+
customerId: string,
|
|
20
|
+
email: @s.matches(Reventless.Pii.mark(~subject="customerId", S.string)) string,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@schema
|
|
24
|
+
type inboundCommand = unit
|
|
25
|
+
|
|
26
|
+
// Keyed by the address, as the shop's translations are: a change is new work.
|
|
27
|
+
let collect = (event, ~sourceId as _) =>
|
|
28
|
+
switch event {
|
|
29
|
+
| AddressGiven({customerId, email}) => [(`${customerId}:${email}`, {customerId, email})]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let translate = async (_id, item: outboundItem, ~capabilities as _) => {
|
|
33
|
+
sent := sent.contents->Array.concat([item.email])
|
|
34
|
+
Ok(None)
|
|
35
|
+
}
|
|
36
|
+
let onExhausted = (_id, _item, ~lastError as _) => None
|
|
37
|
+
|
|
38
|
+
let maxRetries = 3
|
|
39
|
+
let targetName = None
|
|
40
|
+
let sourceNames: array<string> = []
|
|
41
|
+
let externalSystem = None
|
|
42
|
+
let capabilityNeeds: array<Reventless.CapabilityNeed.t> = []
|
|
43
|
+
let traits: array<Reventless.Trait.t> = []
|
|
44
|
+
}
|
|
45
|
+
module VerifyTranslation = {
|
|
46
|
+
let collect = VerifySpec.collect
|
|
47
|
+
let translate = VerifySpec.translate
|
|
48
|
+
let onExhausted = VerifySpec.onExhausted
|
|
49
|
+
let moduleUrl = VerifySpec.moduleUrl
|
|
50
|
+
}
|
|
51
|
+
module Callback = ReventlessCore.OutboundTranslationSlice_Callback.Make(
|
|
52
|
+
VerifySpec,
|
|
53
|
+
VerifyTranslation,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
let toBase64 = (s: string) =>
|
|
57
|
+
NodeCrypto.bufferFromString(s, "utf8")->NodeCrypto.bufferToString("base64")
|
|
58
|
+
let fromBase64 = (s: string) =>
|
|
59
|
+
NodeCrypto.bufferFromString(s, "base64")->NodeCrypto.bufferToString("utf8")
|
|
60
|
+
|
|
61
|
+
/** "Encrypts" by base64; `erased` makes every key gone, as an erasure does. */
|
|
62
|
+
let erased = ref(false)
|
|
63
|
+
let provider: Reventless.Pii.provider = {
|
|
64
|
+
name: "fake",
|
|
65
|
+
encrypts: true,
|
|
66
|
+
seal: async (~subject, ~field as _, ~forgettable as _, json) => Ok(
|
|
67
|
+
Reventless.Pii.wrap(`enc:v1:${subject}:fake:${json->JSON.stringify->toBase64}`),
|
|
68
|
+
),
|
|
69
|
+
prefetch: async _ => Ok(),
|
|
70
|
+
openNow: (~field, json) =>
|
|
71
|
+
switch json->Reventless.Pii.unwrap->Option.flatMap(Reventless.Pii.parseStored) {
|
|
72
|
+
| Some(Cipher(_)) if erased.contents => Ok(Reventless.Pii.wrap(Reventless.Pii.erasedMarker))
|
|
73
|
+
| Some(Cipher({bytes})) => Ok(bytes->fromBase64->JSON.parseOrThrow)
|
|
74
|
+
| _ => Error(Reventless.Pii.UnknownForm({field: field}))
|
|
75
|
+
},
|
|
76
|
+
release: async (~subject as _, ~ref as _) => Ok(),
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let keyed: Reventless.Capabilities.t = {
|
|
80
|
+
...Reventless.Capabilities.none,
|
|
81
|
+
secrets: Reventless.Secrets.fixed(
|
|
82
|
+
~token="t",
|
|
83
|
+
~hash=s => s,
|
|
84
|
+
~fingerprint=(~purpose, input) =>
|
|
85
|
+
`fp(${purpose})#${input->toBase64->String.length->Int.toString}`,
|
|
86
|
+
),
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let given = VerifySpec.AddressGiven({customerId: "c-1", email: "ada@example.org"})
|
|
90
|
+
let rows = () => Callback.todoItems->Dict.toArray
|
|
91
|
+
let publish: ReventlessInfra.CommandTopic.publishJsons = async _ => ()
|
|
92
|
+
|
|
93
|
+
@val external env: dict<string> = "process.env"
|
|
94
|
+
|
|
95
|
+
describe("an outbound work item with personal data", () => {
|
|
96
|
+
beforeEach(() => {
|
|
97
|
+
Callback.todoItems->Dict.keysToArray->Array.forEach(k => Callback.todoItems->Dict.delete(k))
|
|
98
|
+
sent := []
|
|
99
|
+
erased := false
|
|
100
|
+
Reventless.Pii.Provider.install(provider)
|
|
101
|
+
})
|
|
102
|
+
afterEach(() => {
|
|
103
|
+
Reventless.Pii.Provider.install(Reventless.Pii.plain)
|
|
104
|
+
env->Dict.delete("REVENTLESS_PII_REQUIRE_ENCRYPTION")
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
testPromise("is stored sealed, under a key that holds no address", async () => {
|
|
108
|
+
await Callback.phase1([("c-1", given)], ~capabilities=keyed)
|
|
109
|
+
let stored = rows()->JSON.stringifyAny->Option.getOr("")
|
|
110
|
+
expect(stored->String.includes("ada@example.org"))->toBe(false)
|
|
111
|
+
switch rows() {
|
|
112
|
+
| [(id, row)] =>
|
|
113
|
+
expect(id->String.startsWith("fp(outboundWorkItem:VerifyAddress)"))->toBe(true)
|
|
114
|
+
// A sealed string stays a string, so the work list still serves it as one.
|
|
115
|
+
let email = row.item->JSON.Decode.object->Option.flatMap(d => d->Dict.get("email"))
|
|
116
|
+
expect(
|
|
117
|
+
email
|
|
118
|
+
->Option.flatMap(JSON.Decode.string)
|
|
119
|
+
->Option.mapOr(false, s => s->String.startsWith("enc:v1:c-1:")),
|
|
120
|
+
)->toBe(true)
|
|
121
|
+
| _ => fail("expected one row")
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
testPromise("is collected once, however often its event arrives", async () => {
|
|
126
|
+
await Callback.phase1([("c-1", given)], ~capabilities=keyed)
|
|
127
|
+
await Callback.phase1([("c-1", given)], ~capabilities=keyed)
|
|
128
|
+
expect(rows()->Array.length)->toBe(1)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
testPromise("is opened before it is translated", async () => {
|
|
132
|
+
await Callback.phase1([("c-1", given)], ~capabilities=keyed)
|
|
133
|
+
await Callback.phase2(publish, ~capabilities=keyed)
|
|
134
|
+
expect(sent.contents)->toEqual(["ada@example.org"])
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
testPromise("is completed without a call once its subject is erased", async () => {
|
|
138
|
+
await Callback.phase1([("c-1", given)], ~capabilities=keyed)
|
|
139
|
+
erased := true
|
|
140
|
+
await Callback.phase2(publish, ~capabilities=keyed)
|
|
141
|
+
expect(sent.contents)->toEqual([])
|
|
142
|
+
expect(rows()->Array.map(((_, row)) => row.status))->toEqual([
|
|
143
|
+
ReventlessCore.OutboundTranslationSlice_Callback.Completed,
|
|
144
|
+
])
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
testPromise("keeps the key it was given where nothing can fingerprint it", async () => {
|
|
148
|
+
await Callback.phase1([("c-1", given)], ~capabilities=Reventless.Capabilities.none)
|
|
149
|
+
expect(rows()->Array.map(Pair.first))->toEqual(["c-1:ada@example.org"])
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
testPromise(
|
|
153
|
+
"is not stored at all where encryption is required and nothing can fingerprint it",
|
|
154
|
+
async () => {
|
|
155
|
+
env->Dict.set("REVENTLESS_PII_REQUIRE_ENCRYPTION", "true")
|
|
156
|
+
await Callback.phase1([("c-1", given)], ~capabilities=Reventless.Capabilities.none)
|
|
157
|
+
expect(rows())->toEqual([])
|
|
158
|
+
},
|
|
159
|
+
)
|
|
160
|
+
})
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Sury from "sury";
|
|
4
|
+
import * as JestGlobals from "@reventlessdev/rescript-jest/src/JestGlobals.res.mjs";
|
|
5
|
+
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
6
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
7
|
+
import * as Nodebuffer from "node:buffer";
|
|
8
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
9
|
+
import * as Pii$Reventless from "@reventlessdev/reventless-spec/src/components/Pii.res.mjs";
|
|
10
|
+
import * as Secrets$Reventless from "@reventlessdev/reventless-spec/src/semantic/Secrets.res.mjs";
|
|
11
|
+
import * as Capabilities$Reventless from "@reventlessdev/reventless-spec/src/semantic/Capabilities.res.mjs";
|
|
12
|
+
import * as OutboundTranslationSlice_Callback$ReventlessCore from "@reventlessdev/reventless-core/src/components/OutboundTranslationSlice/OutboundTranslationSlice_Callback.res.mjs";
|
|
13
|
+
|
|
14
|
+
let sent = {
|
|
15
|
+
contents: []
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
let name = "VerifyAddress";
|
|
19
|
+
|
|
20
|
+
let moduleUrl = import.meta.url;
|
|
21
|
+
|
|
22
|
+
let consumedEventSchema = Sury.$schema(s => ({
|
|
23
|
+
TAG: "AddressGiven",
|
|
24
|
+
customerId: s.m(Sury.string),
|
|
25
|
+
email: s.m(Sury.string)
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
let outboundItemSchema = Sury.$schema(s => ({
|
|
29
|
+
customerId: s.m(Sury.string),
|
|
30
|
+
email: s.m(Pii$Reventless.mark("customerId", undefined, Sury.string))
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
let inboundCommandSchema = Sury.$unit;
|
|
34
|
+
|
|
35
|
+
function collect(event, param) {
|
|
36
|
+
let email = event.email;
|
|
37
|
+
let customerId = event.customerId;
|
|
38
|
+
return [[
|
|
39
|
+
customerId + `:` + email,
|
|
40
|
+
{
|
|
41
|
+
customerId: customerId,
|
|
42
|
+
email: email
|
|
43
|
+
}
|
|
44
|
+
]];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function translate(_id, item, param) {
|
|
48
|
+
sent.contents = sent.contents.concat([item.email]);
|
|
49
|
+
return {
|
|
50
|
+
TAG: "Ok",
|
|
51
|
+
_0: undefined
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function onExhausted(_id, _item, param) {
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let sourceNames = [];
|
|
60
|
+
|
|
61
|
+
let capabilityNeeds = [];
|
|
62
|
+
|
|
63
|
+
let traits = [];
|
|
64
|
+
|
|
65
|
+
let VerifySpec = {
|
|
66
|
+
name: name,
|
|
67
|
+
moduleUrl: moduleUrl,
|
|
68
|
+
consumedEventSchema: consumedEventSchema,
|
|
69
|
+
outboundItemSchema: outboundItemSchema,
|
|
70
|
+
inboundCommandSchema: inboundCommandSchema,
|
|
71
|
+
collect: collect,
|
|
72
|
+
translate: translate,
|
|
73
|
+
onExhausted: onExhausted,
|
|
74
|
+
maxRetries: 3,
|
|
75
|
+
targetName: undefined,
|
|
76
|
+
sourceNames: sourceNames,
|
|
77
|
+
externalSystem: undefined,
|
|
78
|
+
capabilityNeeds: capabilityNeeds,
|
|
79
|
+
traits: traits
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
let VerifyTranslation = {
|
|
83
|
+
collect: collect,
|
|
84
|
+
translate: translate,
|
|
85
|
+
onExhausted: onExhausted,
|
|
86
|
+
moduleUrl: moduleUrl
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
let Callback = OutboundTranslationSlice_Callback$ReventlessCore.Make({
|
|
90
|
+
name: name,
|
|
91
|
+
moduleUrl: moduleUrl,
|
|
92
|
+
consumedEventSchema: consumedEventSchema,
|
|
93
|
+
outboundItemSchema: outboundItemSchema,
|
|
94
|
+
inboundCommandSchema: inboundCommandSchema,
|
|
95
|
+
maxRetries: 3,
|
|
96
|
+
targetName: undefined,
|
|
97
|
+
sourceNames: sourceNames,
|
|
98
|
+
externalSystem: undefined,
|
|
99
|
+
capabilityNeeds: capabilityNeeds,
|
|
100
|
+
traits: traits
|
|
101
|
+
})(VerifyTranslation);
|
|
102
|
+
|
|
103
|
+
function toBase64(s) {
|
|
104
|
+
return Nodebuffer.Buffer.from(s, "utf8").toString("base64");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function fromBase64(s) {
|
|
108
|
+
return Nodebuffer.Buffer.from(s, "base64").toString("utf8");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let erased = {
|
|
112
|
+
contents: false
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
async function provider_seal(subject, param, param$1, json) {
|
|
116
|
+
return {
|
|
117
|
+
TAG: "Ok",
|
|
118
|
+
_0: Pii$Reventless.wrap(`enc:v1:` + subject + `:fake:` + toBase64(JSON.stringify(json)))
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function provider_prefetch(param) {
|
|
123
|
+
return {
|
|
124
|
+
TAG: "Ok",
|
|
125
|
+
_0: undefined
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function provider_openNow(field, json) {
|
|
130
|
+
let match = Stdlib_Option.flatMap(Pii$Reventless.unwrap(json), Pii$Reventless.parseStored);
|
|
131
|
+
if (match === undefined) {
|
|
132
|
+
return {
|
|
133
|
+
TAG: "Error",
|
|
134
|
+
_0: {
|
|
135
|
+
TAG: "UnknownForm",
|
|
136
|
+
field: field
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
switch (match.TAG) {
|
|
141
|
+
case "Cipher" :
|
|
142
|
+
if (erased.contents) {
|
|
143
|
+
return {
|
|
144
|
+
TAG: "Ok",
|
|
145
|
+
_0: Pii$Reventless.wrap(Pii$Reventless.erasedMarker)
|
|
146
|
+
};
|
|
147
|
+
} else {
|
|
148
|
+
return {
|
|
149
|
+
TAG: "Ok",
|
|
150
|
+
_0: JSON.parse(fromBase64(match.bytes))
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
case "ErasedForm" :
|
|
154
|
+
case "ForgottenForm" :
|
|
155
|
+
return {
|
|
156
|
+
TAG: "Error",
|
|
157
|
+
_0: {
|
|
158
|
+
TAG: "UnknownForm",
|
|
159
|
+
field: field
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function provider_release(param, param$1) {
|
|
166
|
+
return {
|
|
167
|
+
TAG: "Ok",
|
|
168
|
+
_0: undefined
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let provider = {
|
|
173
|
+
name: "fake",
|
|
174
|
+
encrypts: true,
|
|
175
|
+
seal: provider_seal,
|
|
176
|
+
prefetch: provider_prefetch,
|
|
177
|
+
openNow: provider_openNow,
|
|
178
|
+
release: provider_release
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
let keyed_geocode = Capabilities$Reventless.none.geocode;
|
|
182
|
+
|
|
183
|
+
let keyed_messaging = Capabilities$Reventless.none.messaging;
|
|
184
|
+
|
|
185
|
+
let keyed_identityProvider = Capabilities$Reventless.none.identityProvider;
|
|
186
|
+
|
|
187
|
+
let keyed_secrets = Secrets$Reventless.fixed("t", s => s, (purpose, input) => `fp(` + purpose + `)#` + toBase64(input).length.toString());
|
|
188
|
+
|
|
189
|
+
let keyed_stash = Capabilities$Reventless.none.stash;
|
|
190
|
+
|
|
191
|
+
let keyed = {
|
|
192
|
+
geocode: keyed_geocode,
|
|
193
|
+
messaging: keyed_messaging,
|
|
194
|
+
identityProvider: keyed_identityProvider,
|
|
195
|
+
secrets: keyed_secrets,
|
|
196
|
+
stash: keyed_stash
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
let given = {
|
|
200
|
+
TAG: "AddressGiven",
|
|
201
|
+
customerId: "c-1",
|
|
202
|
+
email: "ada@example.org"
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
function rows() {
|
|
206
|
+
return Object.entries(Callback.todoItems);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function publish(param) {
|
|
210
|
+
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
globalThis.describe("an outbound work item with personal data", () => {
|
|
214
|
+
globalThis.beforeEach(() => {
|
|
215
|
+
Object.keys(Callback.todoItems).forEach(k => Stdlib_Dict.$$delete(Callback.todoItems, k));
|
|
216
|
+
sent.contents = [];
|
|
217
|
+
erased.contents = false;
|
|
218
|
+
Pii$Reventless.Provider.install(provider);
|
|
219
|
+
});
|
|
220
|
+
globalThis.afterEach(() => {
|
|
221
|
+
Pii$Reventless.Provider.install(Pii$Reventless.plain);
|
|
222
|
+
Stdlib_Dict.$$delete(process.env, "REVENTLESS_PII_REQUIRE_ENCRYPTION");
|
|
223
|
+
});
|
|
224
|
+
globalThis.test("is stored sealed, under a key that holds no address", async () => {
|
|
225
|
+
await Callback.phase1([[
|
|
226
|
+
"c-1",
|
|
227
|
+
given
|
|
228
|
+
]], keyed);
|
|
229
|
+
let stored = Stdlib_Option.getOr(JSON.stringify(Object.entries(Callback.todoItems)), "");
|
|
230
|
+
globalThis.expect(stored.includes("ada@example.org")).toBe(false);
|
|
231
|
+
let match = Object.entries(Callback.todoItems);
|
|
232
|
+
if (match.length !== 1) {
|
|
233
|
+
return JestGlobals.fail("expected one row");
|
|
234
|
+
}
|
|
235
|
+
let match$1 = match[0];
|
|
236
|
+
globalThis.expect(match$1[0].startsWith("fp(outboundWorkItem:VerifyAddress)")).toBe(true);
|
|
237
|
+
let email = Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(match$1[1].item), d => d["email"]);
|
|
238
|
+
globalThis.expect(Stdlib_Option.mapOr(Stdlib_Option.flatMap(email, Stdlib_JSON.Decode.string), false, s => s.startsWith("enc:v1:c-1:"))).toBe(true);
|
|
239
|
+
});
|
|
240
|
+
globalThis.test("is collected once, however often its event arrives", async () => {
|
|
241
|
+
await Callback.phase1([[
|
|
242
|
+
"c-1",
|
|
243
|
+
given
|
|
244
|
+
]], keyed);
|
|
245
|
+
await Callback.phase1([[
|
|
246
|
+
"c-1",
|
|
247
|
+
given
|
|
248
|
+
]], keyed);
|
|
249
|
+
globalThis.expect(Object.entries(Callback.todoItems).length).toBe(1);
|
|
250
|
+
});
|
|
251
|
+
globalThis.test("is opened before it is translated", async () => {
|
|
252
|
+
await Callback.phase1([[
|
|
253
|
+
"c-1",
|
|
254
|
+
given
|
|
255
|
+
]], keyed);
|
|
256
|
+
await Callback.phase2(publish, keyed);
|
|
257
|
+
globalThis.expect(sent.contents).toEqual(["ada@example.org"]);
|
|
258
|
+
});
|
|
259
|
+
globalThis.test("is completed without a call once its subject is erased", async () => {
|
|
260
|
+
await Callback.phase1([[
|
|
261
|
+
"c-1",
|
|
262
|
+
given
|
|
263
|
+
]], keyed);
|
|
264
|
+
erased.contents = true;
|
|
265
|
+
await Callback.phase2(publish, keyed);
|
|
266
|
+
globalThis.expect(sent.contents).toEqual([]);
|
|
267
|
+
globalThis.expect(Object.entries(Callback.todoItems).map(param => param[1].status)).toEqual(["Completed"]);
|
|
268
|
+
});
|
|
269
|
+
globalThis.test("keeps the key it was given where nothing can fingerprint it", async () => {
|
|
270
|
+
await Callback.phase1([[
|
|
271
|
+
"c-1",
|
|
272
|
+
given
|
|
273
|
+
]], Capabilities$Reventless.none);
|
|
274
|
+
globalThis.expect(Object.entries(Callback.todoItems).map(prim => prim[0])).toEqual(["c-1:ada@example.org"]);
|
|
275
|
+
});
|
|
276
|
+
globalThis.test("is not stored at all where encryption is required and nothing can fingerprint it", async () => {
|
|
277
|
+
process.env["REVENTLESS_PII_REQUIRE_ENCRYPTION"] = "true";
|
|
278
|
+
await Callback.phase1([[
|
|
279
|
+
"c-1",
|
|
280
|
+
given
|
|
281
|
+
]], Capabilities$Reventless.none);
|
|
282
|
+
globalThis.expect(Object.entries(Callback.todoItems)).toEqual([]);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
export {
|
|
287
|
+
sent,
|
|
288
|
+
VerifySpec,
|
|
289
|
+
VerifyTranslation,
|
|
290
|
+
Callback,
|
|
291
|
+
toBase64,
|
|
292
|
+
fromBase64,
|
|
293
|
+
erased,
|
|
294
|
+
provider,
|
|
295
|
+
keyed,
|
|
296
|
+
given,
|
|
297
|
+
rows,
|
|
298
|
+
publish,
|
|
299
|
+
}
|
|
300
|
+
/* moduleUrl Not a pure module */
|