diff --git a/health-and-repair/go/clients/meshconditionsclient/findings/findings.go b/health-and-repair/go/clients/meshconditionsclient/findings/findings.go deleted file mode 100644 index 903eb29..0000000 --- a/health-and-repair/go/clients/meshconditionsclient/findings/findings.go +++ /dev/null @@ -1,88 +0,0 @@ -// Package findings is a part's own bucket `findings` as the state the seat mesh-conditions' client writes into -// (meshconditionsclient.FindingState), so a part gives mesh-sdk's views the FindingWriter of the seat's layout without -// each writing the same adapter over its bucket (novox/hq ADR 0564 §2, ADR 0635 §4). -package findings - -import ( - "context" - "encoding/json" - "errors" - - "github.com/nats-io/nats.go/jetstream" - - "git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient" - "git.novox.be/novox/mesh-sdk/go/views" -) - -// ErrUnbound says the bucket is not on the bus yet, so the finding is not written (the part says it in its log). -var ErrUnbound = errors.New("the findings state is not bound yet") - -// State is the bucket the part's state `findings` is, given when it is bound (process.AwaitedBucket.KV): nil is not bound yet. -type State struct { - Ctx context.Context - KV func() jetstream.KeyValue -} - -var _ meshconditionsclient.FindingState = State{} - -// Put writes one value under its key as JSON. -func (s State) Put(key string, value any) (uint64, error) { - kv := s.KV() - if kv == nil { - return 0, ErrUnbound - } - data, err := json.Marshal(value) - if err != nil { - return 0, err - } - return kv.Put(s.Ctx, key, data) -} - -// Delete clears one key. -func (s State) Delete(key string) error { - kv := s.KV() - if kv == nil { - return ErrUnbound - } - return kv.Delete(s.Ctx, key) -} - -// Writer is the part's views.FindingWriter over its bucket; the function is what views.ProcessReader.Findings and the -// emitter's Findings take. -func Writer(ctx context.Context, kv func() jetstream.KeyValue) views.FindingWriter { - return meshconditionsclient.FindingWriter{State: State{Ctx: ctx, KV: kv}} -} - -// Funcs is a findings state held as the two functions a part keeps over it (the assigner's findings, which also say -// its conditions): a value is put as JSON. -type Funcs struct { - PutBytes func(key string, value []byte) error - DeleteKey func(key string) error -} - -var _ meshconditionsclient.FindingState = Funcs{} - -// Put writes one value under its key as JSON; the state not bound (no function) is ErrUnbound. -func (f Funcs) Put(key string, value any) (uint64, error) { - if f.PutBytes == nil { - return 0, ErrUnbound - } - data, err := json.Marshal(value) - if err != nil { - return 0, err - } - return 0, f.PutBytes(key, data) -} - -// Delete clears one key. -func (f Funcs) Delete(key string) error { - if f.DeleteKey == nil { - return ErrUnbound - } - return f.DeleteKey(key) -} - -// WriterOver is the part's views.FindingWriter over its two functions. -func WriterOver(put func(string, []byte) error, del func(string) error) views.FindingWriter { - return meshconditionsclient.FindingWriter{State: Funcs{PutBytes: put, DeleteKey: del}} -} diff --git a/health-and-repair/go/clients/meshconditionsclient/findings/findings_test.go b/health-and-repair/go/clients/meshconditionsclient/findings/findings_test.go deleted file mode 100644 index 01052ac..0000000 --- a/health-and-repair/go/clients/meshconditionsclient/findings/findings_test.go +++ /dev/null @@ -1,71 +0,0 @@ -package findings - -import ( - "context" - "encoding/json" - "errors" - "testing" - "time" - - "github.com/nats-io/nats.go/jetstream" - - "git.novox.be/novox/mesh-sdk/go/views" -) - -// fakeKV is the two calls a findings state makes of its bucket. -type fakeKV struct { - jetstream.KeyValue - held map[string][]byte -} - -func (f *fakeKV) Put(_ context.Context, key string, value []byte) (uint64, error) { - f.held[key] = value - return uint64(len(f.held)), nil -} - -func (f *fakeKV) Delete(_ context.Context, key string, _ ...jetstream.KVDeleteOpt) error { - delete(f.held, key) - return nil -} - -// A part's finding is written in the seat mesh-conditions' layout under the key the condition keeper reads, cleared by the -// same finding, and refused while the bucket is not bound (the part says that in its log). -func TestAFindingIsWrittenInTheSeatsLayoutAndClearedAndRefusedUnbound(t *testing.T) { - kv := &fakeKV{held: map[string][]byte{}} - var bound jetstream.KeyValue - w := Writer(context.Background(), func() jetstream.KeyValue { return bound }) - f := views.Finding{Observer: "mesh-delivery-teller", Kind: views.UnansweredKind, ID: "mesh-delivery-teller.asks", Summary: "s", Headline: "h", - Explanation: "e", At: time.Date(2026, 11, 5, 15, 0, 0, 0, time.UTC)} - if err := w.Put(f); !errors.Is(err, ErrUnbound) { - t.Fatalf("an unbound bucket took the finding: %v", err) - } - bound = kv - if err := w.Put(f); err != nil { - t.Fatal(err) - } - var got struct{ Key, Kind, Observer, Source string } - for key, value := range kv.held { - if err := json.Unmarshal(value, &got); err != nil || got.Key != key || got.Kind != views.UnansweredKind || - got.Observer != f.Observer || got.Source != f.Observer+"."+f.Kind { - t.Fatalf("%s holds %s (%v)", key, value, err) - } - } - if len(kv.held) != 1 { - t.Fatalf("the bucket holds %d findings, want 1", len(kv.held)) - } - if err := w.Delete(f); err != nil || len(kv.held) != 0 { - t.Fatalf("the finding is not cleared: %v, %d left", err, len(kv.held)) - } - // The same finding over two functions (a part whose findings also say its conditions). - held := map[string][]byte{} - o := WriterOver(func(k string, v []byte) error { held[k] = v; return nil }, func(k string) error { delete(held, k); return nil }) - if err := o.Put(f); err != nil || len(held) != 1 { - t.Fatalf("over functions: %v, %d held", err, len(held)) - } - if err := o.Delete(f); err != nil || len(held) != 0 { - t.Fatalf("over functions not cleared: %v, %d left", err, len(held)) - } - if err := WriterOver(nil, nil).Put(f); !errors.Is(err, ErrUnbound) { - t.Fatalf("no functions took the finding: %v", err) - } -}