feat(redmine): partial UpdateVersion and GetVersion with fake endpoints
UpdateVersion issues PUT /versions/N.json sending only the fields the
caller set (untouched attributes never travel), and never parses the
empty 204 body Redmine answers with. GetVersion backs re-fetching one
milestone. The fake serves both endpoints with Redmine's semantics
(partial apply, 204 empty, 404 for unknown ids), and table tests pin
the exact request bodies, partiality, and error mapping.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
@@ -249,6 +249,98 @@ func TestVersionRoundTrip(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestUpdateVersionPartial drives PUT /versions/N.json (Redmine answers
|
||||
// 204 with an EMPTY body — the client must not try to parse it) and
|
||||
// asserts the partial-update discipline: only provided fields travel.
|
||||
func TestUpdateVersionPartial(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
params redmine.VersionParams
|
||||
wantBody string
|
||||
}{
|
||||
{"status flip", redmine.VersionParams{Status: "closed"}, `{"version":{"status":"closed"}}`},
|
||||
{"due change", redmine.VersionParams{DueDate: "2026-09-15"}, `{"version":{"due_date":"2026-09-15"}}`},
|
||||
{"name change", redmine.VersionParams{Name: "Beta 2"}, `{"version":{"name":"Beta 2"}}`},
|
||||
{"combined flags", redmine.VersionParams{Name: "Beta 2", DueDate: "2026-09-15", Status: "closed"},
|
||||
`{"version":{"due_date":"2026-09-15","name":"Beta 2","status":"closed"}}`},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c, srv := newClient(t)
|
||||
id := srv.AddVersion(fakeredmine.Version{Name: "Beta", DueDate: "2026-08-31", Status: "open"})
|
||||
|
||||
if err := c.UpdateVersion(context.Background(), id, tt.params); err != nil {
|
||||
t.Fatalf("UpdateVersion: %v", err)
|
||||
}
|
||||
got, ok := srv.Version(id)
|
||||
if !ok {
|
||||
t.Fatalf("version %d vanished", id)
|
||||
}
|
||||
for k, v := range map[string]string{
|
||||
"name": got.Name, "due": got.DueDate, "status": got.Status,
|
||||
} {
|
||||
if v == "" {
|
||||
t.Errorf("field %q wiped by partial update: %+v", k, got)
|
||||
}
|
||||
}
|
||||
if tt.params.Name != "" && got.Name != tt.params.Name {
|
||||
t.Errorf("name = %q, want %q", got.Name, tt.params.Name)
|
||||
}
|
||||
if tt.params.DueDate != "" && got.DueDate != tt.params.DueDate {
|
||||
t.Errorf("due = %q, want %q", got.DueDate, tt.params.DueDate)
|
||||
}
|
||||
if tt.params.Status != "" && got.Status != tt.params.Status {
|
||||
t.Errorf("status = %q, want %q", got.Status, tt.params.Status)
|
||||
}
|
||||
// only provided flags are sent — assert the exact PUT body
|
||||
last := srv.Requests()[len(srv.Requests())-1]
|
||||
if last.Method != "PUT" || last.Path != "/versions/"+itoa(id)+".json" {
|
||||
t.Errorf("request = %+v", last)
|
||||
}
|
||||
if last.Body != tt.wantBody {
|
||||
t.Errorf("body = %s, want %s", last.Body, tt.wantBody)
|
||||
}
|
||||
// untouched attributes survive
|
||||
if tt.params.Name == "" && got.Name != "Beta" {
|
||||
t.Errorf("untouched name clobbered: %+v", got)
|
||||
}
|
||||
if tt.params.DueDate == "" && got.DueDate != "2026-08-31" {
|
||||
t.Errorf("untouched due clobbered: %+v", got)
|
||||
}
|
||||
if tt.params.Status == "" && got.Status != "open" {
|
||||
t.Errorf("untouched status clobbered: %+v", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetVersion(t *testing.T) {
|
||||
c, srv := newClient(t)
|
||||
id := srv.AddVersion(fakeredmine.Version{Name: "Beta", DueDate: "2026-08-31", Status: "open"})
|
||||
|
||||
v, err := c.GetVersion(context.Background(), id)
|
||||
if err != nil {
|
||||
t.Fatalf("GetVersion: %v", err)
|
||||
}
|
||||
if v.ID != id || v.Name != "Beta" || v.DueDate != "2026-08-31" || v.Status != "open" {
|
||||
t.Errorf("version = %+v", v)
|
||||
}
|
||||
if _, err := c.GetVersion(context.Background(), 424242); !errors.Is(err, redmine.ErrNotFound) {
|
||||
t.Errorf("missing version err = %v, want ErrNotFound", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateVersionMissing(t *testing.T) {
|
||||
c, _ := newClient(t)
|
||||
err := c.UpdateVersion(context.Background(), 424242, redmine.VersionParams{Status: "closed"})
|
||||
if !errors.Is(err, redmine.ErrNotFound) {
|
||||
t.Fatalf("err = %v, want ErrNotFound", err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "http 404") {
|
||||
t.Fatalf("err = %q, want embedded http 404", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCategoryRoundTrip(t *testing.T) {
|
||||
c, _ := newClient(t)
|
||||
cat, err := c.CreateCategory(context.Background(), "MOPAC", "Secrets")
|
||||
|
||||
Reference in New Issue
Block a user