The client sealed any transport-supplied joules figure as HwShunt, in all three clients (Rust lib.rs, Go jclaw_hydra.go, Python client.py), though TransportResponse.joules_uj carries no grade: a transport with an estimate, or a figure of unknown origin, had it sealed as a hardware reading. TransportResponse now carries the grade of the meter that produced the figure (Rust joules_provenance: Option<Provenance>, plus with_joules; Go JoulesProvenance string; Python joules_provenance), and one rule, graded_joules / GradedJoules, decides what is sealed in every client: the figure at its stated grade; an ungraded figure (or, in Go/Python, a grade that is not HwShunt/ModelBased/Estimator) as Estimator; no figure as a zero Estimator. The conformance vectors carry no touch and are unchanged; Go and Python still verify 9/9. Tests (each fails before on HwShunt): Rust an_ungraded_transport_figure_ is_sealed_as_an_estimate, a_graded_transport_figure_keeps_its_grade; Go TestUngradedJoulesAreSealedAsEstimates, TestGradedJoulesKeepTheirGrade; Python tests/test_energy_grade.py (pytest). Co-Authored-By: Claude Opus 5.5 <[email protected]>
164 lines
5.1 KiB
Go
164 lines
5.1 KiB
Go
package jclawhydra
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
// Conformance test — load the shared vectors and assert every one
|
|
// produces a byte-equal CallReport against the Rust producer's
|
|
// expectations. BLAKE3 hashes match across Rust + Python + Go.
|
|
|
|
func shapeGraphForTests() *ShapeGraph {
|
|
min1 := 1
|
|
max1 := 1
|
|
dtString := DTString
|
|
return NewShapeGraph().Add(NodeShape{
|
|
TargetClass: "https://example.com/Tool#AppendInput",
|
|
Properties: []PropertyShape{
|
|
{Path: "parent", MinCount: &min1, MaxCount: &max1, Datatype: &dtString},
|
|
{Path: "kind", MinCount: &min1, MaxCount: &max1, Datatype: &dtString,
|
|
In: []interface{}{"blob", "text"}},
|
|
},
|
|
})
|
|
}
|
|
|
|
func loadConformance(t *testing.T) map[string]interface{} {
|
|
t.Helper()
|
|
root, err := filepath.Abs(filepath.Join("..", "..", "conformance", "call-reports.json"))
|
|
if err != nil {
|
|
t.Fatalf("abs: %v", err)
|
|
}
|
|
b, err := os.ReadFile(root)
|
|
if err != nil {
|
|
t.Fatalf("read %s: %v", root, err)
|
|
}
|
|
var doc map[string]interface{}
|
|
if err := json.Unmarshal(b, &doc); err != nil {
|
|
t.Fatalf("parse: %v", err)
|
|
}
|
|
return doc
|
|
}
|
|
|
|
func TestCallReportsConformance(t *testing.T) {
|
|
doc := loadConformance(t)
|
|
apiBytes, _ := json.Marshal(doc["api"])
|
|
api, err := ParseAPI(apiBytes)
|
|
if err != nil {
|
|
t.Fatalf("parse api: %v", err)
|
|
}
|
|
client := NewClient(api, shapeGraphForTests())
|
|
transport := NewMockTransport()
|
|
|
|
reports := doc["call_reports"].([]interface{})
|
|
for _, raw := range reports {
|
|
v := raw.(map[string]interface{})
|
|
label := v["label"].(string)
|
|
op := v["operation"].(string)
|
|
input := v["input"]
|
|
expected := v["expected"].(map[string]interface{})
|
|
|
|
got, err := client.Call(op, input, transport)
|
|
if err != nil {
|
|
t.Errorf("FAIL %s: %v", label, err)
|
|
continue
|
|
}
|
|
|
|
if exp := expected["tool_id"].(string); got.ToolID != exp {
|
|
t.Errorf("FAIL %s tool_id expected=%s got=%s", label, exp, got.ToolID)
|
|
}
|
|
if exp := int(expected["status"].(float64)); got.Status != exp {
|
|
t.Errorf("FAIL %s status expected=%d got=%d", label, exp, got.Status)
|
|
}
|
|
if exp := expected["input_hash"].(string); got.InputHash != exp {
|
|
t.Errorf("FAIL %s input_hash expected=%s got=%s", label, exp, got.InputHash)
|
|
}
|
|
if exp := expected["output_hash"].(string); got.OutputHash != exp {
|
|
t.Errorf("FAIL %s output_hash expected=%s got=%s", label, exp, got.OutputHash)
|
|
}
|
|
if exp := expected["status_matches_contract"].(bool); got.StatusMatchesContract != exp {
|
|
t.Errorf("FAIL %s status_matches expected=%v got=%v", label, exp, got.StatusMatchesContract)
|
|
}
|
|
if !reflect.DeepEqual(got.Body, expected["body"]) {
|
|
t.Errorf("FAIL %s body expected=%v got=%v", label, expected["body"], got.Body)
|
|
} else {
|
|
t.Logf("PASS %s", label)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidationConformance(t *testing.T) {
|
|
doc := loadConformance(t)
|
|
apiBytes, _ := json.Marshal(doc["api"])
|
|
api, _ := ParseAPI(apiBytes)
|
|
client := NewClient(api, shapeGraphForTests())
|
|
cases, ok := doc["validation_reports"].([]interface{})
|
|
if !ok {
|
|
t.Skip("no validation_reports")
|
|
}
|
|
for _, raw := range cases {
|
|
v := raw.(map[string]interface{})
|
|
label := v["label"].(string)
|
|
opName := v["operation"].(string)
|
|
input := v["input"]
|
|
expected := v["expected_conforms"].(bool)
|
|
op := client.FindOperation(opName)
|
|
if op == nil {
|
|
t.Errorf("FAIL %s op-not-found", label)
|
|
continue
|
|
}
|
|
err := client.ValidateInput(op, input)
|
|
conforms := err == nil
|
|
if conforms != expected {
|
|
t.Errorf("FAIL %s validation expected=%v got=%v err=%v", label, expected, conforms, err)
|
|
} else {
|
|
t.Logf("PASS %s", label)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A transport's joule figure says nothing of how it was obtained: an
|
|
// ungraded figure is sealed as an estimate, never as a hardware reading.
|
|
func TestUngradedJoulesAreSealedAsEstimates(t *testing.T) {
|
|
doc := loadConformance(t)
|
|
apiBytes, _ := json.Marshal(doc["api"])
|
|
api, err := ParseAPI(apiBytes)
|
|
if err != nil {
|
|
t.Fatalf("parse api: %v", err)
|
|
}
|
|
client := NewClient(api, shapeGraphForTests())
|
|
reports := doc["call_reports"].([]interface{})
|
|
v := reports[0].(map[string]interface{})
|
|
op := v["operation"].(string)
|
|
opID := client.FindOperation(op).ID
|
|
seven := uint64(7)
|
|
|
|
transport := NewMockTransport().Respond(opID, &TransportResponse{Status: 200, Body: map[string]interface{}{}, JoulesUJ: &seven})
|
|
got, err := client.Call(op, v["input"], transport)
|
|
if err != nil {
|
|
t.Fatalf("call: %v", err)
|
|
}
|
|
if got.Touch["joules_uj"] != uint64(7) || got.Touch["energy_provenance"] != "Estimator" {
|
|
t.Errorf("an ungraded figure sealed as %v", got.Touch)
|
|
}
|
|
}
|
|
|
|
// A graded figure keeps the grade the transport gave it.
|
|
func TestGradedJoulesKeepTheirGrade(t *testing.T) {
|
|
nine := uint64(9)
|
|
for _, grade := range []string{"HwShunt", "ModelBased", "Estimator"} {
|
|
r := &TransportResponse{JoulesUJ: &nine, JoulesProvenance: grade}
|
|
if uj, p := r.GradedJoules(); uj != 9 || p != grade {
|
|
t.Errorf("%s: sealed %d %s", grade, uj, p)
|
|
}
|
|
}
|
|
if _, p := (&TransportResponse{JoulesUJ: &nine, JoulesProvenance: "Measured"}).GradedJoules(); p != "Estimator" {
|
|
t.Errorf("an unknown grade sealed as %s", p)
|
|
}
|
|
if uj, p := (&TransportResponse{JoulesProvenance: "HwShunt"}).GradedJoules(); uj != 0 || p != "Estimator" {
|
|
t.Errorf("no figure sealed as %d %s", uj, p)
|
|
}
|
|
}
|