Files
llmgw/internal/clientapi/routes_test.go
T
Ilker ManapandClaude Opus 5.5 b6bfc33ef7 Any ComfyUI workflow runs as a job, from the inputs its catalog entry declares
A model's catalog entry now says what its workflow takes and makes:
comfyui.inputs, each a placeholder of its name with a type (text, int,
float, bool, choice, image, mask, audio, video, file), bounds, choices
and a default; and comfyui.output, which gains video, audio and files
beside pictures and meshes. POST /v1/workflows/runs with {model, inputs}
checks a request against them before a job is written — a required input
left out, an unknown one, a number out of bounds, a song sent as a
picture, another owner's file id are each refused with what was wrong —
and the job worker replays it to a replica, where the files go into
ComfyUI's input directory, the workflow is filled and run, and the first
file of the model's output kind is the job's result. /v1/models lists
each ComfyUI model's workflow, so a client can build its form.

A PNG result carries the licence in an iTXt chunk, as a GLB does in
asset.extras. A file whose server named no type is typed by its name
before its bytes, so a WAV is audio/wav, not audio/wave.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 14:28:15 +02:00

78 lines
2.3 KiB
Go

package clientapi
import (
"os"
"path/filepath"
"regexp"
"runtime"
"slices"
"strings"
"testing"
)
// routerRoutes are the client API's routes as the router mounts them, read
// from its source: every mux.HandleFunc under /v1, /mcp and /me, with the
// path constants it builds some of them from put back.
func routerRoutes(t *testing.T) []string {
t.Helper()
_, file, _, _ := runtime.Caller(0)
dir := filepath.Join(filepath.Dir(file), "..", "router")
consts := map[string]string{"imagesPath": "/v1/images/generations", "meshesPath": "/v1/3d/generations",
"workflowsPath": "/v1/workflows/runs"}
literal := regexp.MustCompile(`mux\.HandleFunc\("([A-Z]+) (/[^"]*)"`)
built := regexp.MustCompile(`mux\.HandleFunc\("([A-Z]+) "\+([A-Za-z]+Path)(\+"([^"]*)")?`)
files, _ := filepath.Glob(filepath.Join(dir, "*.go"))
var out []string
add := func(method, path string) {
if strings.HasPrefix(path, "/v1/") || path == "/mcp" || path == "/me" {
out = append(out, method+" "+path)
}
}
for _, f := range files {
if strings.HasSuffix(f, "_test.go") {
continue
}
b, _ := os.ReadFile(f)
for _, m := range literal.FindAllStringSubmatch(string(b), -1) {
add(m[1], m[2])
}
for _, m := range built.FindAllStringSubmatch(string(b), -1) {
c, ok := consts[m[2]]
if !ok {
t.Fatalf("a route built from %s, which this test does not know", m[2])
}
add(m[1], c+m[4])
}
}
slices.Sort(out)
return slices.Compact(out)
}
func TestEveryClientRouteIsDescribedAndNothingElse(t *testing.T) {
// An endpoint added to the router and not to this table would be missing
// from the document every SDK is generated from; one here and not in the
// router would be a path that answers 404.
mounted := routerRoutes(t)
if len(mounted) < 30 {
t.Fatalf("read only %d routes from the router: %v", len(mounted), mounted)
}
var described []string
for _, r := range Routes {
described = append(described, r.Method+" "+r.Path)
}
slices.Sort(described)
for _, m := range mounted {
if m == "GET /v1/openapi.json" {
continue // the document itself
}
if !slices.Contains(described, m) {
t.Errorf("%s is mounted and not described in clientapi.Routes", m)
}
}
for _, d := range described {
if !slices.Contains(mounted, d) {
t.Errorf("%s is described and not mounted", d)
}
}
}