954efde474
db.Skills() satisfies skill.SkillStore over SQLite, same JSON-blob + indexed columns pattern. Versions live in their own table (each SkillVersion embeds a full Skill snapshot as JSON), ordered newest-first by an append seq. Test: round-trip (Tools, ExposeAsChatbotTool), visibility listing (public/shared/private with SharedWith filtered in Go), chatbot-exposed, newest-first versions + GetVersionByID, scheduled-due query + MarkScheduledRun. contrib/store now covers budget + persona + skill; audit store next. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
73 lines
2.3 KiB
Go
73 lines
2.3 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.stevedudenhoeffer.com/steve/executus/skill"
|
|
)
|
|
|
|
func TestSQLiteSkillStore(t *testing.T) {
|
|
ctx := context.Background()
|
|
db, err := Open(":memory:")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer db.Close()
|
|
st := db.Skills()
|
|
if err := st.Initialize(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
pub := &skill.Skill{ID: "a", Name: "pub", OwnerID: "o1", Visibility: skill.VisibilityPublic,
|
|
Tools: []string{"summarize"}, ExposeAsChatbotTool: true}
|
|
shared := &skill.Skill{ID: "b", Name: "shr", OwnerID: "o1", Visibility: skill.VisibilityShared, SharedWith: []string{"bob"}}
|
|
if err := st.Save(ctx, pub); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := st.Save(ctx, shared); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
got, err := st.Get(ctx, "a")
|
|
if err != nil || len(got.Tools) != 1 || !got.ExposeAsChatbotTool {
|
|
t.Fatalf("round-trip: %v %+v", err, got)
|
|
}
|
|
if ps, _ := st.ListPublic(ctx); len(ps) != 1 || ps[0].ID != "a" {
|
|
t.Errorf("ListPublic = %+v", ps)
|
|
}
|
|
if ss, _ := st.ListSharedWith(ctx, "bob"); len(ss) != 1 || ss[0].ID != "b" {
|
|
t.Errorf("ListSharedWith(bob) = %+v", ss)
|
|
}
|
|
if ss, _ := st.ListSharedWith(ctx, "carol"); len(ss) != 0 {
|
|
t.Errorf("ListSharedWith(carol) should be empty: %+v", ss)
|
|
}
|
|
if ce, _ := st.ListChatbotExposed(ctx); len(ce) != 1 {
|
|
t.Errorf("ListChatbotExposed = %d, want 1", len(ce))
|
|
}
|
|
|
|
// Versions newest-first + by id.
|
|
st.AppendVersion(ctx, skill.SkillVersion{ID: "v1", SkillID: "a", Version: "1.0.0"})
|
|
st.AppendVersion(ctx, skill.SkillVersion{ID: "v2", SkillID: "a", Version: "1.1.0"})
|
|
vs, _ := st.ListVersionsBySkill(ctx, "a", 10)
|
|
if len(vs) != 2 || vs[0].ID != "v2" {
|
|
t.Errorf("versions newest-first: %+v", vs)
|
|
}
|
|
if gv, err := st.GetVersionByID(ctx, "v1"); err != nil || gv.Version != "1.0.0" {
|
|
t.Errorf("GetVersionByID: %v %+v", err, gv)
|
|
}
|
|
|
|
// Scheduling.
|
|
now := time.Now().UTC()
|
|
cron := &skill.Skill{ID: "c", Name: "cron", OwnerID: "o1", Schedule: "0 * * * *", NextRunAt: now.Add(-time.Minute)}
|
|
st.Save(ctx, cron)
|
|
if due, _ := st.ListDueScheduled(ctx, now); len(due) != 1 || due[0].ID != "c" {
|
|
t.Fatalf("ListDueScheduled = %+v", due)
|
|
}
|
|
st.MarkScheduledRun(ctx, "c", now, now.Add(time.Hour))
|
|
if due, _ := st.ListDueScheduled(ctx, now); len(due) != 0 {
|
|
t.Errorf("after MarkScheduledRun nothing due: %+v", due)
|
|
}
|
|
}
|