Files
mesh-tools/node-tools/internal/launch/state_failed_test.go
T

65 lines
1.9 KiB
Go

package launch
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"
)
type refusingState struct{ subscribing }
func (*refusingState) State(string, json.RawMessage) (json.RawMessage, error) {
return nil, errors.New("the bus did not answer in 10s")
}
// A bundle that asks the runtime for a state once it is started, as a tool does.
const askingBundle = `#!/bin/sh
while IFS= read -r line; do
id=$(printf '%s' "$line" | sed -n 's/.*"id":\([0-9]*\)[,}].*/\1/p')
case "$line" in
*'"method":"initialize"'*) printf '{"jsonrpc":"2.0","id":%s,"result":{}}\n' "$id" ;;
*'"method":"tools/list"'*) printf '{"jsonrpc":"2.0","id":%s,"result":{"tools":[]}}\n' "$id"
printf '{"jsonrpc":"2.0","id":"q1","method":"mesh/state.get","params":{"state":"copied","key":"k"}}\n' ;;
esac
done
`
// A state operation the bus refuses or lets time out is said in the runtime's log with its method, its bundle and its
// time (issue 1966: a hand-act seat whose calls hung said nothing).
func TestAFailedStateOperationIsSaidInTheRuntimesLog(t *testing.T) {
dir := t.TempDir()
entry := filepath.Join(dir, "bundle")
if err := os.WriteFile(entry, []byte(askingBundle), 0o755); err != nil {
t.Fatal(err)
}
var mu sync.Mutex
var lines []string
logf := func(f string, a ...any) {
mu.Lock()
defer mu.Unlock()
lines = append(lines, fmt.Sprintf(f, a...))
}
l, err := Start("asker", entry, os.Environ(), &refusingState{}, logf)
if err != nil {
t.Fatal(err)
}
t.Cleanup(l.Stop)
deadline := time.Now().Add(5 * time.Second)
for time.Now().Before(deadline) {
mu.Lock()
all := strings.Join(lines, "\n")
mu.Unlock()
if strings.Contains(all, "asker's bundle asked mesh/state.get and it failed after") && strings.Contains(all, "did not answer in 10s") {
return
}
time.Sleep(20 * time.Millisecond)
}
t.Fatalf("the failed state operation was not said; the log: %v", lines)
}