Files
sundynix-agentix/sundynix-dispatcher/internal/eino/checkpoint_test.go
T
Blizzard f3bf42c432 feat(hitl): 增量1 —— JetStream KV checkpoint store(持久化中断地基)
HITL 持久化中断/恢复的地基:compose checkpoint 需要一个可持久化、抗重启的
存储后端。dispatcher 是"只说 NATS、无 DB"的纯净设计,故复用既有 JetStream
(bus.js)开 KV 桶,不引 Redis、不破坏架构原则。

- shared/bus: 新增 KVHandle + Bus.Checkpoints(bucket, ttl)。薄封装把 NATS
  细节(ErrKeyNotFound→ok=false、Delete 幂等)挡在 bus 内,对外是朴素
  Get/Put/Delete;bus 无需反向依赖 eino。File 存储 + 桶级 TTL 兜底清理。
- dispatcher/eino: checkpointStore 把 CheckpointKV 适配成 compose.CheckPointStore
  (Get/Set + 可选 Delete)。CheckpointKV 是最小接口,bus.KVHandle 结构化满足。
- 测试: 内存桩往返(Set→Get→Delete→miss)+ 编译期契约断言
  `var _ CheckpointKV = (*bus.KVHandle)(nil)` 钉死 bus↔eino 隐式契约。

零爆炸半径(纯新增)。go test ./... 全绿。
下一步增量2:审批节点改 compose.Interrupt + Orchestrator 识别中断置 waiting。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 11:39:20 +08:00

68 lines
1.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package eino
import (
"context"
"sync"
"testing"
"github.com/sundynix/sundynix-shared/bus"
)
// memKV 是 CheckpointKV 的内存桩(并发安全),用于不依赖 NATS 的单测。
type memKV struct {
mu sync.Mutex
m map[string][]byte
}
func newMemKV() *memKV { return &memKV{m: map[string][]byte{}} }
func (k *memKV) Get(_ context.Context, key string) ([]byte, bool, error) {
k.mu.Lock()
defer k.mu.Unlock()
v, ok := k.m[key]
return v, ok, nil
}
func (k *memKV) Put(_ context.Context, key string, val []byte) error {
k.mu.Lock()
defer k.mu.Unlock()
k.m[key] = append([]byte(nil), val...)
return nil
}
func (k *memKV) Delete(_ context.Context, key string) error {
k.mu.Lock()
defer k.mu.Unlock()
delete(k.m, key)
return nil
}
// bus.KVHandleJetStream KV 后端)须结构化满足 CheckpointKV——钉死 bus 与 eino 的隐式契约,
// 任一侧改了 Get/Put/Delete 签名都会在这里编译失败。
var _ CheckpointKV = (*bus.KVHandle)(nil)
// TestCheckpointStoreRoundtrip 验证 compose.CheckPointStore 适配:Set→Get 命中、Delete 后 Get 落空。
func TestCheckpointStoreRoundtrip(t *testing.T) {
s := newCheckpointStore(newMemKV())
ctx := context.Background()
const id = "task_42"
if _, ok, err := s.Get(ctx, id); err != nil || ok {
t.Fatalf("空 store 应未命中: ok=%v err=%v", ok, err)
}
cp := []byte("compose-serialized-state")
if err := s.Set(ctx, id, cp); err != nil {
t.Fatalf("Set: %v", err)
}
got, ok, err := s.Get(ctx, id)
if err != nil || !ok || string(got) != string(cp) {
t.Fatalf("Get 应回放原状态: got=%q ok=%v err=%v", got, ok, err)
}
if err := s.Delete(ctx, id); err != nil {
t.Fatalf("Delete: %v", err)
}
if _, ok, _ := s.Get(ctx, id); ok {
t.Fatalf("Delete 后不应再命中")
}
}