diff --git a/integrationtest/errors_test.go b/integrationtest/errors_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3d43f09580025e2a915758a3b4486791e73b00df --- /dev/null +++ b/integrationtest/errors_test.go @@ -0,0 +1,93 @@ +package integrationtest + +import ( + "encoding/json" + "errors" + "net/http" + "testing" + + as "git.autistici.org/ai3/accountserver" +) + +func TestIntegration_Error_GetUser(t *testing.T) { + stop, _, c := startService(t) + defer stop() + + testdata := []struct { + reqUser string + authUser string + authGroup string + expectedErrCode int + }{ + {"uno@investici.org", "due@investici.org", "", http.StatusForbidden}, + {"zero@investici.org", testAdminUser, testAdminGroup, http.StatusNotFound}, + {"", testAdminUser, testAdminGroup, http.StatusNotFound}, + } + + for _, td := range testdata { + var user as.User + var groups []string + if td.authGroup != "" { + groups = append(groups, td.authGroup) + } + err := c.request("/api/user/get", &as.GetUserRequest{ + UserRequestBase: as.UserRequestBase{ + RequestBase: as.RequestBase{ + SSO: c.ssoTicket(td.authUser, groups...), + }, + Username: td.reqUser, + }, + }, &user) + if err == nil { + t.Errorf("oops, request for %s as %s succeeded unexpectedly", td.reqUser, td.authUser) + continue + } + var httpErr *httpError + if !errors.As(err, &httpErr) { + t.Errorf("oops, request for %s as %s returned unexpected error: %v", td.reqUser, td.authUser, err) + continue + } + if httpErr.code != td.expectedErrCode { + t.Errorf("request for %s as %s returned error code %d, expected %d", td.reqUser, td.authUser, httpErr.code, td.expectedErrCode) + } + } + +} + +func TestIntegration_Error_Validation(t *testing.T) { + stop, _, c := startService(t) + defer stop() + + var resp as.CreateUserResponse + err := c.request("/api/user/create", &as.CreateUserRequest{ + AdminRequestBase: as.AdminRequestBase{ + RequestBase: as.RequestBase{ + SSO: c.ssoTicket(testAdminUser), + }, + }, + User: &as.User{ + Name: "%@example.com", + Resources: []*as.Resource{ + &as.Resource{ + Type: as.ResourceTypeEmail, + Name: "%@example.com", + }, + }, + }, + }, &resp) + if err == nil { + t.Fatal("CreateUser() succeeded unexpectedly") + } + var httpErr *httpError + if !errors.As(err, &httpErr) { + t.Fatalf("CreateUser() did not return a structured error: %v", err) + } + + var fields map[string][]string + if err := json.Unmarshal(httpErr.data, &fields); err != nil { + t.Fatalf("error unmarshaling JSON error: %v", err) + } + if values, ok := fields["name"]; !ok || len(values) != 1 { + t.Fatalf("error does not contain details for the 'name' field: %+v", fields) + } +} diff --git a/integrationtest/integration_test.go b/integrationtest/integration_test.go index e13e77b90bbbbd982398554da185997f457f5ecb..b08981de54a5757086976f88f5dc7769635a4685 100644 --- a/integrationtest/integration_test.go +++ b/integrationtest/integration_test.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "encoding/json" - "errors" "fmt" "io/ioutil" "log" @@ -116,6 +115,15 @@ func (c *testClient) ssoTicket(username string, groups ...string) string { return signed } +type httpError struct { + code int + data []byte +} + +func (e *httpError) Error() string { + return fmt.Sprintf("HTTP status %d", e.code) +} + func (c *testClient) request(uri string, req, out interface{}) error { data, _ := json.Marshal(req) resp, err := http.Post(c.srvURL+uri, "application/json", bytes.NewReader(data)) @@ -127,11 +135,11 @@ func (c *testClient) request(uri string, req, out interface{}) error { if resp.StatusCode >= 400 && resp.StatusCode < 500 { log.Printf("request error: %s", string(data)) - return errors.New(string(data)) + return &httpError{code: resp.StatusCode, data: data} } if resp.StatusCode != 200 { log.Printf("remote error: %s", string(data)) - return fmt.Errorf("http status code %d", resp.StatusCode) + return &httpError{code: resp.StatusCode, data: data} } if resp.Header.Get("Content-Type") != "application/json" { return fmt.Errorf("unexpected content-type %s", resp.Header.Get("Content-Type"))