543aefde4b
Saving a description writes recall corpus. writeNote embeds it under media:image:<id>, a source no enrollment owns, and the method sits at AuthRead, so any enrolled module could put a small VLM's guess into what Maven knows and have it come back in a later turn as something she believes. The describing half stays a read; save_note is now held to the same source-scope rule WriteFact is, and the stored text carries a marker saying it came off a picture. Three doc comments said the method exists only when vision is enabled and the code says otherwise. The code is right, and storing without describing is the state this box is in, so the comments were corrected rather than the behaviour. A request carrying both data and id used to take the id branch and drop the bytes without a word; it is refused. A media dir that cannot be created and a vision endpoint that is a typo were logged at wiring time and the capability just stayed off, which is the hardest kind of misconfiguration to notice. Both fail at startup. runPrune was the one loop started with a bare go and not in the daemon's WaitGroup, so shutdown did not wait for a prune that was deleting files. Found in review of #72.
73 lines
2.1 KiB
Go
73 lines
2.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"image"
|
|
"image/png"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/kami/maven/internal/config"
|
|
"github.com/kami/maven/internal/ipc"
|
|
"github.com/kami/maven/internal/media"
|
|
"github.com/kami/maven/internal/vision"
|
|
)
|
|
|
|
func testIntake(t *testing.T) *visionIntake {
|
|
t.Helper()
|
|
st := newTestStore(t)
|
|
blobs, err := media.Open(t.TempDir(), 0, 0)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return &visionIntake{
|
|
in: vision.NewIntake(blobs, vision.Disabled{}, 0),
|
|
st: st,
|
|
now: time.Now,
|
|
}
|
|
}
|
|
|
|
// The contract says exactly one of Data or ID. Taking the ID branch and
|
|
// dropping the bytes silently is the worst of the three possible answers: the
|
|
// caller believes it sent a new image and nothing says otherwise.
|
|
func TestDescribeRefusesBothDataAndID(t *testing.T) {
|
|
v := testIntake(t)
|
|
_, err := v.describe(context.Background(), ipc.DescribeImageReq{
|
|
Data: []byte("bytes"), ID: strings.Repeat("a", 64),
|
|
})
|
|
if err == nil {
|
|
t.Fatal("both data and id must be refused")
|
|
}
|
|
if !strings.Contains(err.Error(), "send one") {
|
|
t.Fatalf("err = %v, want it to name the contract", err)
|
|
}
|
|
}
|
|
|
|
// Vision being off does not remove the method: the bytes are stored and the
|
|
// answer says she cannot read the picture yet, which is re-runnable by id. That
|
|
// is the state this box is in today, and three doc comments used to claim the
|
|
// opposite.
|
|
func TestVisionOffStillStores(t *testing.T) {
|
|
v := testIntake(t)
|
|
var buf bytes.Buffer
|
|
if err := png.Encode(&buf, image.NewRGBA(image.Rect(0, 0, 4, 4))); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
resp, err := v.describe(context.Background(), ipc.DescribeImageReq{Data: buf.Bytes(), Source: "web:upload"})
|
|
if err != nil {
|
|
t.Fatalf("storing must succeed even with no vision model: %v", err)
|
|
}
|
|
if len(resp.ID) != 64 {
|
|
t.Fatalf("no blob id came back: %+v", resp)
|
|
}
|
|
if resp.Description != "" {
|
|
t.Errorf("description = %q, want none", resp.Description)
|
|
}
|
|
// And with no media block at all the method does not exist.
|
|
if vi := newVisionIntake(nil, nil, nil, &config.Config{}); vi != nil {
|
|
t.Fatal("no media block must leave the method nonexistent")
|
|
}
|
|
}
|