Skip to content

Commit 0056b19

Browse files
committed
Report work package creation accurately
Abort before POST when type resolution fails. Once creation succeeds, report browser-launch problems as warnings so callers do not retry and create duplicates.
1 parent 96f3f12 commit 0056b19

5 files changed

Lines changed: 142 additions & 5 deletions

File tree

cmd/workpackage/create.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package workpackage
22

33
import (
4+
stderrors "errors"
45
"fmt"
56
"strconv"
67

@@ -41,14 +42,16 @@ func createWorkPackage(cmd *cobra.Command, args []string) error {
4142

4243
workPackage, err := work_packages.Create(createProjectId, createOptions(cmd, subject))
4344
if err != nil {
44-
printer.Error(err)
45+
if !stderrors.Is(err, openerrors.ErrHandled) {
46+
printer.Error(err)
47+
}
4548
return openerrors.ErrHandled
4649
}
4750

4851
if createOpenInBrowser {
4952
err = launch.Browser(routes.WorkPackageUrl(workPackage))
5053
if err != nil {
51-
printer.ErrorText(fmt.Sprintf("Error opening browser: %+v", err))
54+
printer.Warning(fmt.Sprintf("Error opening browser: %+v", err))
5255
}
5356
} else {
5457
printer.WorkPackage(workPackage)

cmd/workpackage/create_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package workpackage
2+
3+
import (
4+
"net/http"
5+
"strings"
6+
"testing"
7+
)
8+
9+
func TestCreateBrowserFailureKeepsSuccessfulExit(t *testing.T) {
10+
testingPrinter, requestCount := initWorkPackageTestServer(t, func(response http.ResponseWriter, request *http.Request) {
11+
if request.Method != http.MethodPost {
12+
t.Errorf("request method = %s, want POST", request.Method)
13+
}
14+
response.Header().Set("Content-Type", "application/json")
15+
_, _ = response.Write([]byte(`{"id":42,"displayId":"42","subject":"Created"}`))
16+
})
17+
18+
t.Setenv("PATH", t.TempDir())
19+
createProjectId = "1"
20+
createOpenInBrowser = true
21+
createTypeFlag = ""
22+
createAssigneeFlag = 0
23+
createDescriptionFlag = ""
24+
t.Cleanup(func() {
25+
createProjectId = ""
26+
createOpenInBrowser = false
27+
createTypeFlag = ""
28+
createAssigneeFlag = 0
29+
createDescriptionFlag = ""
30+
})
31+
32+
if err := createWorkPackage(createCmd, []string{"Created"}); err != nil {
33+
t.Fatalf("createWorkPackage returned an error after the API create succeeded: %v", err)
34+
}
35+
36+
if *requestCount != 1 {
37+
t.Errorf("request count = %d, want 1", *requestCount)
38+
}
39+
if !strings.Contains(testingPrinter.ErrResult, "[WARNING]") ||
40+
!strings.Contains(testingPrinter.ErrResult, "Error opening browser") {
41+
t.Errorf("stderr should warn about the browser failure, got %q", testingPrinter.ErrResult)
42+
}
43+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package workpackage
2+
3+
import (
4+
"net/http"
5+
"net/http/httptest"
6+
"net/url"
7+
"testing"
8+
9+
"github.com/opf/openproject-cli/components/printer"
10+
"github.com/opf/openproject-cli/components/requests"
11+
"github.com/opf/openproject-cli/components/routes"
12+
)
13+
14+
func initWorkPackageTestServer(t *testing.T, handler http.HandlerFunc) (*printer.TestingPrinter, *int) {
15+
t.Helper()
16+
17+
requestCount := 0
18+
server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
19+
requestCount++
20+
handler(response, request)
21+
}))
22+
t.Cleanup(server.Close)
23+
24+
host, err := url.Parse(server.URL)
25+
if err != nil {
26+
t.Fatal(err)
27+
}
28+
requests.Init(host, "", false)
29+
routes.Init(host)
30+
31+
testingPrinter := &printer.TestingPrinter{}
32+
printer.Init(testingPrinter)
33+
if err := printer.InitRenderer("json"); err != nil {
34+
t.Fatal(err)
35+
}
36+
t.Cleanup(func() {
37+
_ = printer.InitRenderer("text")
38+
})
39+
40+
return testingPrinter, &requestCount
41+
}

components/resources/work_packages/create.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"strconv"
88

9+
openerrors "github.com/opf/openproject-cli/components/errors"
910
"github.com/opf/openproject-cli/components/parser"
1011
"github.com/opf/openproject-cli/components/paths"
1112
"github.com/opf/openproject-cli/components/printer"
@@ -50,10 +51,8 @@ func typeCreate(projectId string, workPackage *dtos.WorkPackageDto, input string
5051
printer.Cyan(input),
5152
printer.Red(projectId),
5253
))
53-
5454
printer.Types(types.Convert())
55-
56-
return nil
55+
return openerrors.ErrHandled
5756
}
5857

5958
if workPackage.Links == nil {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package work_packages_test
2+
3+
import (
4+
"errors"
5+
"net/http"
6+
"net/http/httptest"
7+
"net/url"
8+
"testing"
9+
10+
openerrors "github.com/opf/openproject-cli/components/errors"
11+
"github.com/opf/openproject-cli/components/printer"
12+
"github.com/opf/openproject-cli/components/requests"
13+
"github.com/opf/openproject-cli/components/resources/work_packages"
14+
)
15+
16+
func TestCreateRejectsInvalidTypeBeforePosting(t *testing.T) {
17+
postCount := 0
18+
server := httptest.NewServer(http.HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
19+
response.Header().Set("Content-Type", "application/json")
20+
switch {
21+
case request.Method == http.MethodGet && request.URL.Path == "/api/v3/projects/1":
22+
_, _ = response.Write([]byte(`{"_links":{"types":{"href":"/api/v3/projects/1/types"}}}`))
23+
case request.Method == http.MethodGet && request.URL.Path == "/api/v3/projects/1/types":
24+
_, _ = response.Write([]byte(`{"_embedded":{"elements":[]}}`))
25+
case request.Method == http.MethodPost:
26+
postCount++
27+
_, _ = response.Write([]byte(`{"id":42}`))
28+
default:
29+
http.Error(response, "unexpected request", http.StatusNotFound)
30+
}
31+
}))
32+
t.Cleanup(server.Close)
33+
34+
host, err := url.Parse(server.URL)
35+
if err != nil {
36+
t.Fatal(err)
37+
}
38+
requests.Init(host, "", false)
39+
printer.Init(&printer.TestingPrinter{})
40+
41+
_, err = work_packages.Create("1", map[work_packages.CreateOption]string{
42+
work_packages.CreateSubject: "Subject",
43+
work_packages.CreateType: "Missing",
44+
})
45+
if !errors.Is(err, openerrors.ErrHandled) {
46+
t.Fatalf("Create error = %v, want ErrHandled", err)
47+
}
48+
if postCount != 0 {
49+
t.Errorf("POST count = %d, want 0", postCount)
50+
}
51+
}

0 commit comments

Comments
 (0)