Restructure refimpl into go-lang and python subdirectories

Move Go reference implementation to refimpl/go-lang/ and add new
Python reference implementation in refimpl/python/. Update build.sh
with renamed draft and simplified tool paths.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 23:11:55 +01:00
parent ff795c72e6
commit bbf557e54b
52 changed files with 3972 additions and 341 deletions

View File

@@ -0,0 +1,140 @@
package ect
import (
"testing"
"time"
)
func TestValidateDAG_Root(t *testing.T) {
store := NewMemoryLedger()
payload := &Payload{
Jti: "jti-001",
Wid: "wf-1",
Par: []string{},
PolDecision: PolDecisionApproved,
}
err := ValidateDAG(payload, store, DefaultDAGConfig())
if err != nil {
t.Fatal(err)
}
}
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}
err := ValidateDAG(payload, store, DefaultDAGConfig())
if err == nil {
t.Fatal("expected error for duplicate jti")
}
}
func TestValidateDAG_ParentExists(t *testing.T) {
store := NewMemoryLedger()
_, _ = store.Append("jws1", &Payload{Jti: "jti-001", Wid: "wf-1", Par: []string{}, PolDecision: PolDecisionApproved, Iat: time.Now().Unix() - 60})
payload := &Payload{
Jti: "jti-002",
Wid: "wf-1",
Par: []string{"jti-001"},
PolDecision: PolDecisionApproved,
Iat: time.Now().Unix(),
}
err := ValidateDAG(payload, store, DefaultDAGConfig())
if err != nil {
t.Fatal(err)
}
}
func TestValidateDAG_ParentNotFound(t *testing.T) {
store := NewMemoryLedger()
payload := &Payload{
Jti: "jti-002",
Par: []string{"jti-missing"},
PolDecision: PolDecisionApproved,
Iat: time.Now().Unix(),
}
err := ValidateDAG(payload, store, DefaultDAGConfig())
if err == nil {
t.Fatal("expected error when parent not found")
}
}
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})
cfg := DAGConfig{ClockSkewTolerance: DefaultClockSkewTolerance, MaxAncestorLimit: 2}
payload := &Payload{Jti: "jti-3", Wid: "wf", Par: []string{"jti-2"}, PolDecision: PolDecisionApproved, Iat: now}
err := ValidateDAG(payload, store, cfg)
if err == nil {
t.Fatal("expected error when ancestor limit exceeded")
}
}
func TestValidateDAG_StoreNil(t *testing.T) {
payload := &Payload{Jti: "j1", Par: []string{}, PolDecision: PolDecisionApproved, Iat: time.Now().Unix()}
err := ValidateDAG(payload, nil, DefaultDAGConfig())
if err == nil {
t.Fatal("expected error when store is nil")
}
}
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}
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}
err = ValidateDAG(payload2, store, DAGConfig{ClockSkewTolerance: 30, MaxAncestorLimit: 10000})
if err == nil {
t.Fatal("expected error when parent not earlier than child")
}
}
func TestValidateDAG_DirectCycle(t *testing.T) {
// par contains own jti (direct self-reference) -> parent not found
store := NewMemoryLedger()
now := time.Now().Unix()
payload := &Payload{Jti: "jti-self", Wid: "wf", Par: []string{"jti-self"}, PolDecision: PolDecisionApproved, Iat: now}
err := ValidateDAG(payload, store, DefaultDAGConfig())
if err == nil {
t.Fatal("expected error for direct cycle (par contains self)")
}
}
func TestValidateDAG_hasCycle_visitedContinue(t *testing.T) {
// par has duplicate parent ID so we hit "if _, ok := visited[parentID]; 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}
err := ValidateDAG(payload, store, DefaultDAGConfig())
if err != nil {
t.Fatal(err)
}
}
func TestValidateDAG_ParentPolicyRejected_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}
err := ValidateDAG(payload, store, DefaultDAGConfig())
if err == nil {
t.Fatal("expected error when parent rejected and no compensation")
}
payload.Ext = map[string]interface{}{"compensation_required": true}
err = ValidateDAG(payload, store, DefaultDAGConfig())
if err != nil {
t.Fatal(err)
}
}