2026-10-04 00:45:23 +00:00
|
|
|
package llm
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
|
|
|
|
"io/fs"
|
|
|
|
|
"os"
|
|
|
|
|
"path"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"testing"
|
|
|
|
|
"testing/fstest"
|
|
|
|
|
|
2026-10-11 01:40:38 +00:00
|
|
|
"gitea.maximumdirect.net/eric/audita/v3/internal/core/config"
|
|
|
|
|
"gitea.maximumdirect.net/eric/audita/v3/internal/prompts"
|
2026-11-10 18:13:12 +00:00
|
|
|
ck "go.maximumdirect.net/configkit"
|
2026-10-04 00:45:23 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func copyEmbeddedPromptFilesystem(t *testing.T) fstest.MapFS {
|
|
|
|
|
t.Helper()
|
|
|
|
|
source := prompts.Filesystem()
|
|
|
|
|
files := make(fstest.MapFS)
|
|
|
|
|
err := fs.WalkDir(source, prompts.AssetRoot, func(name string, entry fs.DirEntry, err error) error {
|
|
|
|
|
if err != nil || entry.IsDir() {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
data, err := fs.ReadFile(source, name)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
files[name] = &fstest.MapFile{Data: data}
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
return files
|
|
|
|
|
}
|
|
|
|
|
|
2026-11-10 18:13:12 +00:00
|
|
|
func TestRuntimeClassifiesEmbeddedAssetsAndHonorsCancellation(t *testing.T) {
|
|
|
|
|
loaded, err := config.Load(ck.Input{Source: ck.NoSource()}, "")
|
2026-10-04 01:10:10 +00:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
2026-11-10 18:13:12 +00:00
|
|
|
_, err = newRuntimeWithPromptFS(context.Background(), loaded, nil, fstest.MapFS{})
|
2026-10-04 00:45:23 +00:00
|
|
|
if err == nil || !IsAssetError(err) || !errors.Is(err, fs.ErrNotExist) {
|
|
|
|
|
t.Fatalf("embedded discovery error was not preserved as an asset error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
|
cancel()
|
2026-11-10 18:13:12 +00:00
|
|
|
_, err = newRuntimeWithPromptFS(ctx, loaded, nil, fstest.MapFS{})
|
2026-10-04 00:45:23 +00:00
|
|
|
if !errors.Is(err, context.Canceled) || IsAssetError(err) {
|
|
|
|
|
t.Fatalf("caller cancellation classification: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestBrokenEmbeddedPromptFailsEmbeddedOnlyConstruction(t *testing.T) {
|
|
|
|
|
promptFS := copyEmbeddedPromptFilesystem(t)
|
2026-11-10 18:13:12 +00:00
|
|
|
assetPath := path.Join(prompts.AssetRoot, "modules.grammar.proposal.yaml")
|
|
|
|
|
promptFS[assetPath] = &fstest.MapFile{Data: []byte("collections:\n audita:\n prompts:\n - id: modules.grammar.proposal\n messages: []\n")}
|
|
|
|
|
loaded, err := config.Load(ck.Input{Source: ck.NoSource()}, "")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
_, err = newRuntimeWithPromptFS(context.Background(), loaded, nil, promptFS)
|
2026-10-04 00:45:23 +00:00
|
|
|
if err == nil || !IsAssetError(err) {
|
|
|
|
|
t.Fatalf("broken embedded prompt was not classified as an asset error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestOperatorPromptCanRepairInvalidEmbeddedPrompt(t *testing.T) {
|
|
|
|
|
promptFS := copyEmbeddedPromptFilesystem(t)
|
2026-11-10 18:13:12 +00:00
|
|
|
assetPath := path.Join(prompts.AssetRoot, "modules.grammar.proposal.yaml")
|
|
|
|
|
promptFS[assetPath] = &fstest.MapFile{Data: []byte("collections:\n audita:\n prompts:\n - id: modules.grammar.proposal\n messages: []\n")}
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
for name, content := range map[string]string{
|
|
|
|
|
"config.yml": "version: 3\n",
|
|
|
|
|
"prompts.yml": "collections:\n audita:\n prompts:\n - id: modules.grammar.proposal\n messages:\n - role: user\n content: operator message\n output:\n format: text\n validation_mode: none\n",
|
|
|
|
|
} {
|
|
|
|
|
if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0600); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
loaded, err := config.Load(ck.Input{Source: ck.File(filepath.Join(dir, "config.yml"))}, "")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
runtime, err := newRuntimeWithPromptFS(context.Background(), loaded, nil, promptFS)
|
2026-10-04 00:45:23 +00:00
|
|
|
if err != nil {
|
2026-11-10 18:13:12 +00:00
|
|
|
t.Fatalf("valid final configuration rejected: %v", err)
|
2026-10-04 00:45:23 +00:00
|
|
|
}
|
|
|
|
|
if runtime.Engine == nil {
|
|
|
|
|
t.Fatal("runtime engine was not retained")
|
|
|
|
|
}
|
|
|
|
|
}
|