package delivery import ( "encoding/json" "errors" "strings" "testing" "time" ) // 1153/AC4: the watchman's module and its three buckets, each that module's state. func TestWatchmanBuckets(t *testing.T) { if WatchmanModule != "mesh-delivery-watchman" { t.Fatalf("WatchmanModule = %q", WatchmanModule) } for name, bucket := range map[string]string{"presence": PresenceBucket, "pulse": PulseBucket, "stuck": StuckBucket} { if bucket != WatchmanModule+"_"+name { t.Errorf("the %s bucket is %q, not %s_%s", name, bucket, WatchmanModule, name) } } } // 1153/AC1: the stuck key is the stopped key's bucket and key, split at the first dot, refused as a pulse key is. func TestStuckKey(t *testing.T) { key, err := StuckKey(PresenceBucket, "ace") if err != nil || key != "mesh-delivery-watchman_presence.ace" { t.Fatalf("StuckKey = %q, %v", key, err) } key, err = StuckKey("mesh-controller_builds", "zsh.build.b-1") if err != nil || key != "mesh-controller_builds.zsh.build.b-1" { t.Fatalf("StuckKey = %q, %v", key, err) } bucket, rest, err := ParseStuckKey(key) if err != nil || bucket != "mesh-controller_builds" || rest != "zsh.build.b-1" { t.Fatalf("ParseStuckKey = %q %q %v", bucket, rest, err) } for _, c := range [][2]string{{"", "k"}, {"a.b", "k"}, {"b", ""}, {"b", "k x"}, {"b", ".k"}, {"b", "k."}, {"b", "k>"}} { if _, err := StuckKey(c[0], c[1]); err == nil { t.Errorf("StuckKey(%q, %q) accepted", c[0], c[1]) } } for _, bad := range []string{"", "nodot", ".k", "b."} { if _, _, err := ParseStuckKey(bad); err == nil { t.Errorf("ParseStuckKey(%q) accepted", bad) } } } // 1153/AC2: a stuck value round-trips with "v":1, and an unknown or missing version is refused loudly. func TestStuckValue(t *testing.T) { s := Stuck{Bucket: PresenceBucket, Key: "ace", Waited: "ace to come back online", Since: time.Date(2026, 10, 18, 20, 0, 0, 0, time.UTC)} raw, err := EncodeStuck(s) if err != nil { t.Fatal(err) } var probe map[string]any if err := json.Unmarshal(raw, &probe); err != nil || probe["v"] != float64(1) { t.Fatalf("encoded %s: v = %v (%v)", raw, probe["v"], err) } back, err := DecodeStuck(raw) s.V = Version if err != nil || !SameStuck(back, s) { t.Fatalf("decoded %+v, %v; want %+v", back, err, s) } for _, bad := range []string{`{"v":2,"bucket":"b","key":"k"}`, `{"bucket":"b","key":"k"}`} { _, err := DecodeStuck([]byte(bad)) if !errors.Is(err, ErrUnknownVersion) { t.Errorf("DecodeStuck(%s) = %v, want ErrUnknownVersion", bad, err) } if strings.Contains(bad, `"v":2`) && (err == nil || !strings.Contains(err.Error(), "2") || !strings.Contains(err.Error(), "1")) { t.Errorf("DecodeStuck(%s) = %v: does not name the version seen and the one read", bad, err) } } } // 1153/AC3: equal stuck values are the same; one moved is not. func TestSameStuck(t *testing.T) { a := Stuck{V: Version, Bucket: "b", Key: "k", Waited: "w", Since: time.Unix(100, 0).UTC()} b := a if !SameStuck(a, b) { t.Fatal("equal values are not the same") } b.Moved = true if SameStuck(a, b) { t.Fatal("a moved value is the same as a stuck one") } }