fix: review findings — chain NaN/Inf + id hygiene, percent-escape jobPath, shared singleVideoResult
- SubmitChain rejects NaN/±Inf segment seconds with ErrUnsupported (previously an obscure json.Marshal error; NaN fails every comparison and +Inf passed the >= 0 check). - ChainStatus skips segment entries with no usable id — JSON null (which no-op-unmarshals into a string, previously appending ""), empty strings, and id-less objects; the unfiltered list survives in Raw. ChainJob.SegmentIDs doc now also says ChainSegmentResult takes the segment index, not an id string. - jobPath rejects '%' in job ids — %2F/%2E%2E percent-escapes decode back into path structure server-side, bypassing the literal check on this upstream-echoed value. - singleVideoResult moves to video.go next to videoMIME, and the two remaining hand-rolled copies of the video-result validation (videoModel.Generate, Interpolate) now use it — one validation, one message shape. - videogen.LipSyncer renamed to videogen.Lipsyncer for consistency with the rest of the surface's Lipsync* naming (LipsyncProvider, LipsyncModel, LipsyncRequest); not yet consumed downstream, so the rename is free now and never again. Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01WWCQcYStWXBYUy5sZWnbLT
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
@@ -111,6 +112,17 @@ func TestSubmitChainRejectsBadArgs(t *testing.T) {
|
||||
}); !errors.Is(err, llm.ErrUnsupported) {
|
||||
t.Errorf("negative seconds: err = %v, want ErrUnsupported", err)
|
||||
}
|
||||
for name, bad := range map[string]float64{
|
||||
"NaN": math.NaN(),
|
||||
"+Inf": math.Inf(1),
|
||||
"-Inf": math.Inf(-1),
|
||||
} {
|
||||
if _, err := ch.SubmitChain(context.Background(), videogen.ChainRequest{
|
||||
Segments: []videogen.ChainSegment{{Prompt: "x", Seconds: bad}},
|
||||
}); !errors.Is(err, llm.ErrUnsupported) {
|
||||
t.Errorf("%s seconds: err = %v, want ErrUnsupported", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitChainRejectsMissingJobID(t *testing.T) {
|
||||
@@ -175,6 +187,27 @@ func TestChainStatusToleratesObjectSegments(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainStatusSkipsIDLessSegments(t *testing.T) {
|
||||
// null, "", an id-less object, and a mistyped entry must all be
|
||||
// skipped — never appended as "" (SegmentIDs promises fetchable
|
||||
// artifacts; the unfiltered list stays in Raw).
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"status":"running","segment":3,"total":5,` +
|
||||
`"segments":[null,"seg-0","",{},{"id":"seg-1"},{"segment_id":""},42]}`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
p := New(WithBaseURL(srv.URL), WithHTTPClient(srv.Client()))
|
||||
ch, _ := p.ChainerModel("videoutils")
|
||||
job, err := ch.ChainStatus(context.Background(), "job-123")
|
||||
if err != nil {
|
||||
t.Fatalf("ChainStatus: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(job.SegmentIDs, []string{"seg-0", "seg-1"}) {
|
||||
t.Errorf("SegmentIDs = %v, want [seg-0 seg-1]", job.SegmentIDs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChainStatusRejectsStatuslessPayload(t *testing.T) {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"detail":"no such job"}`))
|
||||
@@ -193,7 +226,7 @@ func TestChainStatusRejectsStatuslessPayload(t *testing.T) {
|
||||
func TestChainJobPathRejectsHostileIDs(t *testing.T) {
|
||||
p := New(WithBaseURL("http://unused"))
|
||||
ch, _ := p.ChainerModel("videoutils")
|
||||
for _, bad := range []string{"", "a/b", "a?b", "a#b", "..", "a..b"} {
|
||||
for _, bad := range []string{"", "a/b", "a?b", "a#b", "..", "a..b", "a%2Fb", "%2e%2e", "a%b"} {
|
||||
if _, err := ch.ChainStatus(context.Background(), bad); err == nil {
|
||||
t.Errorf("ChainStatus(%q) succeeded; want error", bad)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user