feat: migrate refimpls from draft-00 to draft-01 claim names
- Rename `par` to `pred` (predecessor) in types, serialization, tests - Remove `pol`, `pol_decision` from core payload; move to `ect_ext` - Remove `sub` from payload (not part of ECT spec) - Update `typ` from `wimse-exec+jwt` to `exec+jwt` (accept both) - Rename MaxParLength to MaxPredLength everywhere - Update testdata, demos, READMEs with migration table - All Go tests pass, all 56 Python tests pass (90% coverage)
This commit is contained in:
@@ -8,10 +8,9 @@ import (
|
||||
func TestValidateDAG_Root(t *testing.T) {
|
||||
store := NewMemoryLedger()
|
||||
payload := &Payload{
|
||||
Jti: "jti-001",
|
||||
Wid: "wf-1",
|
||||
Par: []string{},
|
||||
PolDecision: PolDecisionApproved,
|
||||
Jti: "jti-001",
|
||||
Wid: "wf-1",
|
||||
Pred: []string{},
|
||||
}
|
||||
err := ValidateDAG(payload, store, DefaultDAGConfig())
|
||||
if err != nil {
|
||||
@@ -21,23 +20,22 @@ func TestValidateDAG_Root(t *testing.T) {
|
||||
|
||||
func TestValidateDAG_DuplicateJti(t *testing.T) {
|
||||
store := NewMemoryLedger()
|
||||
_, _ = store.Append("dummy-jws", &Payload{Jti: "jti-001", Wid: "wf-1", Par: []string{}, PolDecision: PolDecisionApproved})
|
||||
payload := &Payload{Jti: "jti-001", Wid: "wf-1", Par: []string{}, PolDecision: PolDecisionApproved}
|
||||
_, _ = store.Append("dummy-jws", &Payload{Jti: "jti-001", Wid: "wf-1", Pred: []string{}})
|
||||
payload := &Payload{Jti: "jti-001", Wid: "wf-1", Pred: []string{}}
|
||||
err := ValidateDAG(payload, store, DefaultDAGConfig())
|
||||
if err == nil {
|
||||
t.Fatal("expected error for duplicate jti")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDAG_ParentExists(t *testing.T) {
|
||||
func TestValidateDAG_PredExists(t *testing.T) {
|
||||
store := NewMemoryLedger()
|
||||
_, _ = store.Append("jws1", &Payload{Jti: "jti-001", Wid: "wf-1", Par: []string{}, PolDecision: PolDecisionApproved, Iat: time.Now().Unix() - 60})
|
||||
_, _ = store.Append("jws1", &Payload{Jti: "jti-001", Wid: "wf-1", Pred: []string{}, Iat: time.Now().Unix() - 60})
|
||||
payload := &Payload{
|
||||
Jti: "jti-002",
|
||||
Wid: "wf-1",
|
||||
Par: []string{"jti-001"},
|
||||
PolDecision: PolDecisionApproved,
|
||||
Iat: time.Now().Unix(),
|
||||
Jti: "jti-002",
|
||||
Wid: "wf-1",
|
||||
Pred: []string{"jti-001"},
|
||||
Iat: time.Now().Unix(),
|
||||
}
|
||||
err := ValidateDAG(payload, store, DefaultDAGConfig())
|
||||
if err != nil {
|
||||
@@ -45,17 +43,16 @@ func TestValidateDAG_ParentExists(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDAG_ParentNotFound(t *testing.T) {
|
||||
func TestValidateDAG_PredNotFound(t *testing.T) {
|
||||
store := NewMemoryLedger()
|
||||
payload := &Payload{
|
||||
Jti: "jti-002",
|
||||
Par: []string{"jti-missing"},
|
||||
PolDecision: PolDecisionApproved,
|
||||
Iat: time.Now().Unix(),
|
||||
Jti: "jti-002",
|
||||
Pred: []string{"jti-missing"},
|
||||
Iat: time.Now().Unix(),
|
||||
}
|
||||
err := ValidateDAG(payload, store, DefaultDAGConfig())
|
||||
if err == nil {
|
||||
t.Fatal("expected error when parent not found")
|
||||
t.Fatal("expected error when predecessor not found")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,10 +60,10 @@ func TestValidateDAG_DepthLimit(t *testing.T) {
|
||||
store := NewMemoryLedger()
|
||||
now := time.Now().Unix()
|
||||
// Chain: jti-1 -> jti-2 -> jti-3 -> ...; validate with maxAncestorLimit=2 so we exceed it
|
||||
_, _ = store.Append("jws1", &Payload{Jti: "jti-1", Wid: "wf", Par: []string{}, PolDecision: PolDecisionApproved, Iat: now - 100})
|
||||
_, _ = store.Append("jws2", &Payload{Jti: "jti-2", Wid: "wf", Par: []string{"jti-1"}, PolDecision: PolDecisionApproved, Iat: now - 50})
|
||||
_, _ = store.Append("jws1", &Payload{Jti: "jti-1", Wid: "wf", Pred: []string{}, Iat: now - 100})
|
||||
_, _ = store.Append("jws2", &Payload{Jti: "jti-2", Wid: "wf", Pred: []string{"jti-1"}, Iat: now - 50})
|
||||
cfg := DAGConfig{ClockSkewTolerance: DefaultClockSkewTolerance, MaxAncestorLimit: 2}
|
||||
payload := &Payload{Jti: "jti-3", Wid: "wf", Par: []string{"jti-2"}, PolDecision: PolDecisionApproved, Iat: now}
|
||||
payload := &Payload{Jti: "jti-3", Wid: "wf", Pred: []string{"jti-2"}, Iat: now}
|
||||
err := ValidateDAG(payload, store, cfg)
|
||||
if err == nil {
|
||||
t.Fatal("expected error when ancestor limit exceeded")
|
||||
@@ -74,7 +71,7 @@ func TestValidateDAG_DepthLimit(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateDAG_StoreNil(t *testing.T) {
|
||||
payload := &Payload{Jti: "j1", Par: []string{}, PolDecision: PolDecisionApproved, Iat: time.Now().Unix()}
|
||||
payload := &Payload{Jti: "j1", Pred: []string{}, Iat: time.Now().Unix()}
|
||||
err := ValidateDAG(payload, nil, DefaultDAGConfig())
|
||||
if err == nil {
|
||||
t.Fatal("expected error when store is nil")
|
||||
@@ -84,53 +81,56 @@ func TestValidateDAG_StoreNil(t *testing.T) {
|
||||
func TestValidateDAG_TemporalOrdering(t *testing.T) {
|
||||
store := NewMemoryLedger()
|
||||
now := time.Now().Unix()
|
||||
_, _ = store.Append("jws1", &Payload{Jti: "jti-1", Wid: "wf", Par: []string{}, PolDecision: PolDecisionApproved, Iat: now})
|
||||
// child has iat before parent + skew: parent.iat (now) >= child.iat (now+100) + 30 => invalid
|
||||
payload := &Payload{Jti: "jti-2", Wid: "wf", Par: []string{"jti-1"}, PolDecision: PolDecisionApproved, Iat: now + 100}
|
||||
_, _ = store.Append("jws1", &Payload{Jti: "jti-1", Wid: "wf", Pred: []string{}, Iat: now})
|
||||
// child has iat after pred: valid
|
||||
payload := &Payload{Jti: "jti-2", Wid: "wf", Pred: []string{"jti-1"}, Iat: now + 100}
|
||||
err := ValidateDAG(payload, store, DAGConfig{ClockSkewTolerance: 30, MaxAncestorLimit: 10000})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// parent.iat >= child.iat + skew: parent at now+50, child at now+10, skew 30 => 50 >= 40 => invalid
|
||||
_, _ = store.Append("jws2", &Payload{Jti: "jti-1b", Wid: "wf", Par: []string{}, PolDecision: PolDecisionApproved, Iat: now + 50})
|
||||
payload2 := &Payload{Jti: "jti-2b", Wid: "wf", Par: []string{"jti-1b"}, PolDecision: PolDecisionApproved, Iat: now + 10}
|
||||
// pred.iat >= child.iat + skew: pred at now+50, child at now+10, skew 30 => 50 >= 40 => invalid
|
||||
_, _ = store.Append("jws2", &Payload{Jti: "jti-1b", Wid: "wf", Pred: []string{}, Iat: now + 50})
|
||||
payload2 := &Payload{Jti: "jti-2b", Wid: "wf", Pred: []string{"jti-1b"}, Iat: now + 10}
|
||||
err = ValidateDAG(payload2, store, DAGConfig{ClockSkewTolerance: 30, MaxAncestorLimit: 10000})
|
||||
if err == nil {
|
||||
t.Fatal("expected error when parent not earlier than child")
|
||||
t.Fatal("expected error when predecessor not earlier than child")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDAG_DirectCycle(t *testing.T) {
|
||||
// par contains own jti (direct self-reference) -> parent not found
|
||||
// pred contains own jti (direct self-reference) -> predecessor not found
|
||||
store := NewMemoryLedger()
|
||||
now := time.Now().Unix()
|
||||
payload := &Payload{Jti: "jti-self", Wid: "wf", Par: []string{"jti-self"}, PolDecision: PolDecisionApproved, Iat: now}
|
||||
payload := &Payload{Jti: "jti-self", Wid: "wf", Pred: []string{"jti-self"}, Iat: now}
|
||||
err := ValidateDAG(payload, store, DefaultDAGConfig())
|
||||
if err == nil {
|
||||
t.Fatal("expected error for direct cycle (par contains self)")
|
||||
t.Fatal("expected error for direct cycle (pred contains self)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDAG_hasCycle_visitedContinue(t *testing.T) {
|
||||
// par has duplicate parent ID so we hit "if _, ok := visited[parentID]; ok { continue }"
|
||||
// pred has duplicate predecessor ID so we hit "if _, ok := visited[predID]; ok { continue }"
|
||||
store := NewMemoryLedger()
|
||||
now := time.Now().Unix()
|
||||
_, _ = store.Append("jws1", &Payload{Jti: "jti-a", Wid: "wf", Par: []string{}, PolDecision: PolDecisionApproved, Iat: now - 10})
|
||||
payload := &Payload{Jti: "jti-b", Wid: "wf", Par: []string{"jti-a", "jti-a"}, PolDecision: PolDecisionApproved, Iat: now}
|
||||
_, _ = store.Append("jws1", &Payload{Jti: "jti-a", Wid: "wf", Pred: []string{}, Iat: now - 10})
|
||||
payload := &Payload{Jti: "jti-b", Wid: "wf", Pred: []string{"jti-a", "jti-a"}, Iat: now}
|
||||
err := ValidateDAG(payload, store, DefaultDAGConfig())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDAG_ParentPolicyRejected_RequiresCompensation(t *testing.T) {
|
||||
func TestValidateDAG_PredPolicyRejected_RequiresCompensation(t *testing.T) {
|
||||
store := NewMemoryLedger()
|
||||
now := time.Now().Unix()
|
||||
_, _ = store.Append("jws1", &Payload{Jti: "jti-rej", Wid: "wf", Par: []string{}, Pol: "p", PolDecision: PolDecisionRejected, Iat: now - 60})
|
||||
payload := &Payload{Jti: "jti-child", Wid: "wf", Par: []string{"jti-rej"}, PolDecision: PolDecisionApproved, Iat: now}
|
||||
_, _ = store.Append("jws1", &Payload{
|
||||
Jti: "jti-rej", Wid: "wf", Pred: []string{}, Iat: now - 60,
|
||||
Ext: map[string]interface{}{"pol": "p", "pol_decision": "rejected"},
|
||||
})
|
||||
payload := &Payload{Jti: "jti-child", Wid: "wf", Pred: []string{"jti-rej"}, Iat: now}
|
||||
err := ValidateDAG(payload, store, DefaultDAGConfig())
|
||||
if err == nil {
|
||||
t.Fatal("expected error when parent rejected and no compensation")
|
||||
t.Fatal("expected error when predecessor rejected and no compensation")
|
||||
}
|
||||
payload.Ext = map[string]interface{}{"compensation_required": true}
|
||||
err = ValidateDAG(payload, store, DefaultDAGConfig())
|
||||
|
||||
Reference in New Issue
Block a user