From a319c94b657b2548cc2c2e2500dfb97e93ff6a9b Mon Sep 17 00:00:00 2001
From: ale <ale@incal.net>
Date: Sun, 5 Dec 2021 14:08:59 +0000
Subject: [PATCH] Add tests for errors returned by the HTTP API

---
 integrationtest/errors_test.go      | 93 +++++++++++++++++++++++++++++
 integrationtest/integration_test.go | 14 ++++-
 2 files changed, 104 insertions(+), 3 deletions(-)
 create mode 100644 integrationtest/errors_test.go

diff --git a/integrationtest/errors_test.go b/integrationtest/errors_test.go
new file mode 100644
index 00000000..3d43f095
--- /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 e13e77b9..b08981de 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"))
-- 
GitLab