feat(job-array-one-based-index): xcecli submit shows the array (task 8.3)

Submitting an array prints the server's message with the array ID in
written form (no [k]) and the element count, e.g.
'Job <0-0-...> is submitted to queue <normal> (3 elements).'; ordinary
submits print exactly as before. The client model mirrors array_size.
Submit help documents name[1-N] and name[1], numbering from 1 and the
API-server limit (default 1000, at most 32767).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-01 12:58:36 +00:00
parent 58ca7abb36
commit d3b12159a4
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
4 changed files with 141 additions and 11 deletions

View file

@ -1,8 +1,20 @@
package jobs
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
"go.uber.org/zap"
"xcecli/common/format"
"xcecli/common/request"
"xcecli/config"
"xcecli/models"
)
// bug-1676: BuildJobRequest asserts our own hostname as FromHost rather than
@ -25,3 +37,96 @@ func TestBuildJobRequest_SetsFromHostToLocalHostname(t *testing.T) {
t.Errorf("FromHost = %q, want local hostname %q", req.FromHost, wantHost)
}
}
// submitServer answers every submit with the given response data.
func submitServer(t *testing.T, data models.JobSubmitData) *httptest.Server {
t.Helper()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(models.JobSubmitResponse{Code: 0, Msg: "ok", Data: data})
}))
t.Cleanup(srv.Close)
// The submit path logs through config.Log, which only main initialises.
if config.Log == nil {
config.Log = zap.NewNop()
t.Cleanup(func() { config.Log = nil })
}
return srv
}
// An accepted array submission prints the array identity in written form,
// without an element number, and the server's element count (3.1, 3.2); the
// count is mirrored in the client model for scripted use.
func TestSubmitOutput_ArrayIdentityAndCount(t *testing.T) {
srv := submitServer(t, models.JobSubmitData{
JobID: int64(testArrayKey),
Message: fmt.Sprintf("Job <%d> is submitted to queue <normal> (3 elements).", testArrayKey),
ArraySize: 3,
})
js := NewJobSubmitter(nil)
js.command = "sleep 1"
req, err := js.BuildJobRequest()
if err != nil {
t.Fatalf("BuildJobRequest: %v", err)
}
var resp *models.JobSubmitResponse
out, err := captureStdout(t, func() error {
var e error
resp, e = submitAndReport(request.NewAPIClient(srv.URL), "tok", req)
return e
})
if err != nil {
t.Fatalf("submitAndReport: %v", err)
}
want := "Job <" + testArrayWritten + "> is submitted to queue <normal> (3 elements).\n"
if out != want {
t.Fatalf("output = %q, want %q", out, want)
}
if resp.Data.ArraySize != 3 {
t.Errorf("ArraySize = %d, want 3", resp.Data.ArraySize)
}
}
// An ordinary submission prints exactly as before (3.3): no count added.
func TestSubmitOutput_OrdinaryUnchanged(t *testing.T) {
id := format.MakeJobID(0, 0, 1700000000, 0, 7)
srv := submitServer(t, models.JobSubmitData{
JobID: int64(id),
Message: fmt.Sprintf("Job <%d> is submitted to queue <normal>.", id),
})
js := NewJobSubmitter(nil)
js.command = "sleep 1"
req, err := js.BuildJobRequest()
if err != nil {
t.Fatalf("BuildJobRequest: %v", err)
}
var resp *models.JobSubmitResponse
out, err := captureStdout(t, func() error {
var e error
resp, e = submitAndReport(request.NewAPIClient(srv.URL), "tok", req)
return e
})
if err != nil {
t.Fatalf("submitAndReport: %v", err)
}
want := "Job <0-0-1700000000-7> is submitted to queue <normal>.\n"
if out != want {
t.Fatalf("output = %q, want %q", out, want)
}
if resp.Data.ArraySize != 0 {
t.Errorf("ArraySize = %d, want 0", resp.Data.ArraySize)
}
}
// Submit help describes the array forms, numbering from 1 and the limit (9.1).
func TestSubmitHelpShowsArrayForms(t *testing.T) {
long := NewSubmitJobsCmd(nil).Long
for _, s := range []string{
"-J 'name[1-N]'", "-J 'name[1]'", "start at 1",
"set by the API server", "default 1000", "32767",
} {
if !strings.Contains(long, s) {
t.Errorf("submit help lacks %q", s)
}
}
}