feat(m4): verify signed surface bundles across hosts
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/wailsapp/wails/v3/pkg/application"
|
||||
)
|
||||
|
||||
type goldenFixture struct {
|
||||
Contract string `json:"contract"`
|
||||
Cases []goldenCase `json:"cases"`
|
||||
}
|
||||
|
||||
type goldenCase struct {
|
||||
ID string `json:"id"`
|
||||
Expected goldenExpected `json:"expected"`
|
||||
Envelopes []wireEnvelope `json:"envelopes"`
|
||||
Transitions []hostTransition `json:"transitions"`
|
||||
}
|
||||
|
||||
type goldenExpected struct {
|
||||
ItemKind string `json:"item_kind"`
|
||||
Fallback string `json:"fallback"`
|
||||
KernelToolStatus string `json:"kernel_tool_status"`
|
||||
}
|
||||
|
||||
type hostTransition struct {
|
||||
AfterEnvelope int `json:"after_envelope"`
|
||||
Kind string `json:"kind"`
|
||||
CallID string `json:"call_id"`
|
||||
}
|
||||
|
||||
type wireEnvelope struct {
|
||||
Version int `json:"v"`
|
||||
Type string `json:"type"`
|
||||
Payload map[string]interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
// TestGoldenContract is deliberately independent of Wails' JS bindings. It
|
||||
// proves that a Wails Host can run the exact versioned fixture, and that the
|
||||
// host's admission gate remains data-only before a WebView receives anything.
|
||||
func TestGoldenContract(t *testing.T) {
|
||||
var fixture goldenFixture
|
||||
path := filepath.Join("..", "tauri", "src", "runtime", "golden", "lineup-v1.json")
|
||||
bytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("read shared golden fixture: %v", err)
|
||||
}
|
||||
if err := json.Unmarshal(bytes, &fixture); err != nil {
|
||||
t.Fatalf("parse shared golden fixture: %v", err)
|
||||
}
|
||||
if fixture.Contract != "lineup-v1-golden-1" {
|
||||
t.Fatalf("unexpected golden fixture contract %q", fixture.Contract)
|
||||
}
|
||||
if len(fixture.Cases) != 6 {
|
||||
t.Fatalf("expected six shared cases, got %d", len(fixture.Cases))
|
||||
}
|
||||
|
||||
for _, test := range fixture.Cases {
|
||||
t.Run(test.ID, func(t *testing.T) {
|
||||
status := map[string]string{}
|
||||
kind := ""
|
||||
fallback := ""
|
||||
for index, envelope := range test.Envelopes {
|
||||
kind, fallback = project(envelope)
|
||||
if kind == "tool-call" {
|
||||
status[stringValue(envelope.Payload["call_id"])] = "pending"
|
||||
}
|
||||
if kind == "tool-result" {
|
||||
callID := stringValue(envelope.Payload["call_id"])
|
||||
if status[callID] == "submitted" {
|
||||
status[callID] = stringValue(envelope.Payload["status"])
|
||||
}
|
||||
}
|
||||
for _, transition := range test.Transitions {
|
||||
if transition.AfterEnvelope == index && transition.Kind == "submit_tool_call" && status[transition.CallID] == "pending" {
|
||||
status[transition.CallID] = "submitted"
|
||||
}
|
||||
}
|
||||
}
|
||||
if kind != test.Expected.ItemKind {
|
||||
t.Fatalf("projection = %q, want %q (fallback %q)", kind, test.Expected.ItemKind, fallback)
|
||||
}
|
||||
if fallback != test.Expected.Fallback {
|
||||
t.Fatalf("fallback = %q, want %q", fallback, test.Expected.Fallback)
|
||||
}
|
||||
if test.Expected.KernelToolStatus != "" {
|
||||
matched := false
|
||||
for _, got := range status {
|
||||
matched = matched || got == test.Expected.KernelToolStatus
|
||||
}
|
||||
if !matched {
|
||||
t.Fatalf("no tool lifecycle reached %q: %#v", test.Expected.KernelToolStatus, status)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Compile-time integration boundary: the Spike uses the actual Wails v3 API,
|
||||
// but LineUp never grants a Surface a Wails binding. Any future production
|
||||
// implementation must retain this separation.
|
||||
func TestWailsApplicationBoundaryCompiles(t *testing.T) {
|
||||
var options application.Options
|
||||
if options.Name != "" {
|
||||
t.Fatal("zero-value options changed unexpectedly")
|
||||
}
|
||||
}
|
||||
|
||||
func project(envelope wireEnvelope) (kind, fallback string) {
|
||||
if envelope.Version != 1 {
|
||||
return "fallback", "unsupported_version"
|
||||
}
|
||||
switch envelope.Type {
|
||||
case "lineup.v1.tool.call":
|
||||
if stringValue(envelope.Payload["call_id"]) == "" || stringValue(envelope.Payload["tool"]) == "" {
|
||||
return "fallback", "invalid_payload"
|
||||
}
|
||||
return "tool-call", ""
|
||||
case "lineup.v1.tool.result":
|
||||
if stringValue(envelope.Payload["call_id"]) == "" || stringValue(envelope.Payload["status"]) == "" {
|
||||
return "fallback", "invalid_payload"
|
||||
}
|
||||
return "tool-result", ""
|
||||
case "lineup.v1.ui.open", "lineup.v1.ui.patch", "lineup.v1.ui.close":
|
||||
for _, forbidden := range []string{"bundle", "bundle_id", "bundle_url", "url", "html", "css", "javascript", "js", "script", "source"} {
|
||||
if _, exists := envelope.Payload[forbidden]; exists {
|
||||
return "fallback", "invalid_payload"
|
||||
}
|
||||
}
|
||||
return "surface", ""
|
||||
case "lineup.v1.app.call":
|
||||
if stringValue(envelope.Payload["call_id"]) == "" || stringValue(envelope.Payload["capability"]) == "" || stringValue(envelope.Payload["reason"]) == "" || stringValue(envelope.Payload["expires_at"]) == "" {
|
||||
return "fallback", "invalid_payload"
|
||||
}
|
||||
if _, ok := envelope.Payload["arguments"].(map[string]interface{}); !ok {
|
||||
return "fallback", "invalid_payload"
|
||||
}
|
||||
return "app-call", ""
|
||||
default:
|
||||
return "fallback", "unsupported_type"
|
||||
}
|
||||
}
|
||||
|
||||
func stringValue(value interface{}) string {
|
||||
text, _ := value.(string)
|
||||
return text
|
||||
}
|
||||
Reference in New Issue
Block a user