package skills import ( "context" "io" "log/slog" "os" "testing" "git.yeij.top/AskaEth/Cyrene_v2/internal/db" "git.yeij.top/AskaEth/Cyrene_v2/pkg/plugins/sdk" ) // testLogger returns a logger that writes nowhere, keeping test output clean. func testLogger() *slog.Logger { return slog.New(slog.NewTextHandler(io.Discard, nil)) } // makeSkill builds a deterministic skill populated with one step, one ref, and // one guardrail, so tests can assert the full round trip. func makeSkill(t *testing.T, name string) *Skill { t.Helper() sk, err := New(name, "desc "+name, "prompt "+name, []sdk.Step{{Name: "s1", Prompt: "step prompt"}}, []sdk.Ref{{Name: "r1", Type: "tool", Value: "v1", Optional: false}}, []sdk.Guardrail{{ID: "g1", Message: "msg", Enforced: true}}, ) if err != nil { t.Fatalf("New(%q): %v", name, err) } return sk } // mockSkill is a minimal sdk.Skill used to feed invalid inputs (blank name) and // to exercise the store's generic sdk.Skill handling. type mockSkill struct{ name string } func (m mockSkill) Name() string { return m.name } func (m mockSkill) Description() string { return "desc" } func (m mockSkill) Prompt() string { return "prompt" } func (m mockSkill) Steps() []sdk.Step { return nil } func (m mockSkill) Refs() []sdk.Ref { return nil } func (m mockSkill) Guardrails() []sdk.Guardrail { return nil } // newTestStore opens an isolated in-memory SQLite database and builds a Store // over it. It is cleaned up when the test finishes. func newTestStore(t *testing.T) *Store { t.Helper() ctx := context.Background() database, err := db.Open(ctx, ":memory:", testLogger()) if err != nil { t.Fatalf("db.Open(:memory:): %v", err) } t.Cleanup(func() { _ = database.Close() }) store, err := NewStore(ctx, database, testLogger()) if err != nil { t.Fatalf("NewStore: %v", err) } return store } // writeFile writes content to path, failing the test on error. func writeFile(t *testing.T, path, content string) { t.Helper() if err := os.WriteFile(path, []byte(content), 0o644); err != nil { t.Fatalf("write %s: %v", path, err) } }