115 lines
4.4 KiB
Go
115 lines
4.4 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"infinite-canvas/backend/internal/protocol"
|
|
)
|
|
|
|
func TestPluginViewIncludesDocumentationForEveryBundledProtocol(t *testing.T) {
|
|
center, err := newPluginRuntime(t.TempDir())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
plugins := center.list()
|
|
if len(plugins) != len(protocol.Builtins().List("", "", true)) {
|
|
t.Fatalf("plugin views = %d, builtins = %d", len(plugins), len(protocol.Builtins().List("", "", true)))
|
|
}
|
|
for _, plugin := range plugins {
|
|
if plugin.Source != "bundled" {
|
|
continue
|
|
}
|
|
document := strings.TrimSpace(plugin.Manifest.Protocol.Documentation)
|
|
if document == "" || !strings.Contains(document, "## 影策运行时合同") {
|
|
t.Errorf("bundled plugin %q has no complete documentation in PluginView", plugin.Manifest.ID)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPluginRuntimeIsTheProtocolSourceOfTruth(t *testing.T) {
|
|
dataDir := t.TempDir()
|
|
center, err := newPluginRuntime(dataDir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := center.registrySnapshot().List("", "", false); len(got) == 0 {
|
|
t.Fatal("bundled protocols were not reconciled into a new plugin directory")
|
|
}
|
|
manifest := []byte(`{"apiVersion":"v1","metadata":{"id":"uploaded-runtime","version":"1.0.0","name":"Uploaded Runtime","vendor":"Test","categories":["video"],"scopes":["canvas"],"documentation":"# Uploaded Runtime"},"create":{"method":"POST","path":"/tasks","fields":{"prompt":"request.prompt"}},"response":{"statusPaths":["status"]}}`)
|
|
plugin, err := center.install(manifest)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if plugin.Status != "enabled" || !center.registrySnapshot().IsCapability("uploaded-runtime", protocol.CapabilityVideo) {
|
|
t.Fatalf("installed plugin was not activated: %#v", plugin)
|
|
}
|
|
if _, err := center.install(manifest); err == nil {
|
|
t.Fatal("duplicate plugin id overwrote an installed plugin")
|
|
}
|
|
if _, err := center.setEnabled("uploaded-runtime", false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, ok := center.registrySnapshot().Resolve("uploaded-runtime"); !ok {
|
|
t.Fatal("disabled plugin was removed from registry snapshot instead of being represented as unavailable")
|
|
}
|
|
if center.registrySnapshot().IsCapability("uploaded-runtime", protocol.CapabilityVideo) {
|
|
t.Fatal("disabled plugin remained selectable")
|
|
}
|
|
if err := center.uninstall("uploaded-runtime"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, ok := center.registrySnapshot().Resolve("uploaded-runtime"); ok {
|
|
t.Fatal("uninstalled plugin remained selectable")
|
|
}
|
|
}
|
|
|
|
func TestDeclarativeProtocolRuntimeExecutesCreatePollAndDownload(t *testing.T) {
|
|
manifest := []byte(`{
|
|
"apiVersion":"v1",
|
|
"metadata":{"id":"test-declarative-video-runtime","version":"1.0.0","name":"Test Declarative Video","vendor":"Test","categories":["video"],"scopes":["canvas"],"documentation":"# Test Declarative Video"},
|
|
"create":{"method":"POST","path":"/tasks","fields":{"model":"request.model","prompt":"request.prompt","seconds":"request.duration"}},
|
|
"poll":{"method":"GET","path":"/tasks/{{taskId}}"},
|
|
"response":{"taskIdPaths":["id"],"statusPaths":["status"],"resultUrlPaths":["video_url"],"resultKind":"video"}
|
|
}`)
|
|
center, err := newPluginRuntime(t.TempDir())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := center.install(manifest); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var server *httptest.Server
|
|
server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/v1/tasks":
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"id":"task-1","status":"pending"}`))
|
|
case "/v1/tasks/task-1":
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_, _ = w.Write([]byte(`{"id":"task-1","status":"succeeded","video_url":"` + server.URL + `/media.mp4"}`))
|
|
case "/media.mp4":
|
|
w.Header().Set("Content-Type", "video/mp4")
|
|
_, _ = w.Write([]byte("video"))
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
|
|
config := providerConfig{BaseURL: server.URL + "/v1", APIKey: "key", Model: "test-model", APIFormat: "openai", InterfaceType: "test-declarative-video-runtime", AllowLocalChannel: true}
|
|
ctx := withProviderOutboundPolicy(context.Background(), config)
|
|
ctx = withProtocolRegistry(ctx, center.registrySnapshot())
|
|
result, err := runDeclarativeProtocolTask(ctx, canvasGenerationInput{Mode: "video", Prompt: "a clip", Config: config})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if result["mode"] != "video" {
|
|
t.Fatalf("result = %#v", result)
|
|
}
|
|
}
|