mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-04 12:07:09 +00:00
feat(verifier): OverrideExtractorValues for V8-driven extractors
Web tick path runs extractor bodies in V8 against the real DOM, then overrides goja-side .current slots so LTL predicates evaluate against those values. Mobile callers can pass nil for a no-op.
This commit is contained in:
1 parent
87b30d0f22
commit
0d6f32f8a4
2 files changed
+80
No files matched your search
@@ -532,3 +532,58 @@ globalThis.properties = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOverrideExtractorValues_PreservesPrevious(t *testing.T) {
|
||||||
|
verifier := newVerifier(t)
|
||||||
|
mustLoad(t, verifier, helloSpec)
|
||||||
|
|
||||||
|
if err := verifier.PushSnapshot(SnapshotInput{Snapshots: Snapshots{"ledger.balance": json.RawMessage(`100`)}}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := verifier.OverrideExtractorValues(map[int]json.RawMessage{1: json.RawMessage(`777`)}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
balance := verifier.runtime.GlobalObject().Get("balance").ToObject(verifier.runtime)
|
||||||
|
if balance.Get("current").ToInteger() != 777 {
|
||||||
|
t.Errorf("override didn't take: current=%v", balance.Get("current"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next push: previous mirrors the *override*, not the snapshot value.
|
||||||
|
if err := verifier.PushSnapshot(SnapshotInput{Snapshots: Snapshots{"ledger.balance": json.RawMessage(`200`)}}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
balance = verifier.runtime.GlobalObject().Get("balance").ToObject(verifier.runtime)
|
||||||
|
if balance.Get("previous").ToInteger() != 777 {
|
||||||
|
t.Errorf("previous should reflect override, got %v", balance.Get("previous"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOverrideExtractorValues_NilIsNoop(t *testing.T) {
|
||||||
|
verifier := newVerifier(t)
|
||||||
|
mustLoad(t, verifier, helloSpec)
|
||||||
|
if err := verifier.PushSnapshot(SnapshotInput{Snapshots: Snapshots{"ledger.balance": json.RawMessage(`42`)}}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := verifier.OverrideExtractorValues(nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := verifier.OverrideExtractorValues(map[int]json.RawMessage{}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
balance := verifier.runtime.GlobalObject().Get("balance").ToObject(verifier.runtime)
|
||||||
|
if balance.Get("current").ToInteger() != 42 {
|
||||||
|
t.Errorf("expected snapshot-driven current to remain 42, got %v", balance.Get("current"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOverrideExtractorValues_UnknownIndexErrors(t *testing.T) {
|
||||||
|
verifier := newVerifier(t)
|
||||||
|
mustLoad(t, verifier, helloSpec)
|
||||||
|
if err := verifier.PushSnapshot(SnapshotInput{}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
err := verifier.OverrideExtractorValues(map[int]json.RawMessage{99: json.RawMessage(`1`)})
|
||||||
|
if err == nil || !strings.Contains(err.Error(), "out of range") {
|
||||||
|
t.Errorf("expected out-of-range error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package verifier
|
package verifier
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand/v2"
|
"math/rand/v2"
|
||||||
@@ -238,6 +239,30 @@ func (v *Verifier) PushSnapshot(input SnapshotInput) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OverrideExtractorValues replaces each extractor's `current` slot with a
|
||||||
|
// caller-supplied value, keyed by registration index. Used by the web tick
|
||||||
|
// path so extractor bodies that ran in V8 (against the real DOM) drive the
|
||||||
|
// goja-side LTL predicates without re-running the getter against an empty
|
||||||
|
// state.ax shim. Passing a nil/empty map is a no-op so the mobile path can
|
||||||
|
// call this unconditionally. The override must run *after* PushSnapshot
|
||||||
|
// (which advanced `previous`) and *before* EvaluateProperties.
|
||||||
|
func (v *Verifier) OverrideExtractorValues(overrides map[int]json.RawMessage) error {
|
||||||
|
if len(overrides) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for index, raw := range overrides {
|
||||||
|
if index < 0 || index >= len(v.extractors) {
|
||||||
|
return fmt.Errorf("extractor override index %d out of range (have %d)", index, len(v.extractors))
|
||||||
|
}
|
||||||
|
value, err := jsonToJSValue(v.runtime, raw)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("extractor override %d: %w", index, err)
|
||||||
|
}
|
||||||
|
_ = v.extractors[index].handle.Set("current", value)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// SnapshotInput bundles everything a step feeds into the verifier. Fields
|
// SnapshotInput bundles everything a step feeds into the verifier. Fields
|
||||||
// other than Snapshots are optional; callers that only have snapshots can
|
// other than Snapshots are optional; callers that only have snapshots can
|
||||||
// populate Snapshots alone and leave the rest zero.
|
// populate Snapshots alone and leave the rest zero.
|
||||||
|
|||||||
Reference in new issue
Block a user