add test team member (#1842)
* return err if teams API fails with StatusCode other than 404 * add unit test for 404 at team members Co-authored-by: Jociele Padilha <jociele.padilha@zalando.de> Co-authored-by: Felix Kunde <felix-kunde@gmx.de>
This commit is contained in:
		
							parent
							
								
									9bcb25ac7e
								
							
						
					
					
						commit
						483bf624ee
					
				|  | @ -194,8 +194,8 @@ type mockTeamsAPIClient struct { | ||||||
| 	members []string | 	members []string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *mockTeamsAPIClient) TeamInfo(teamID, token string) (tm *teams.Team, err error) { | func (m *mockTeamsAPIClient) TeamInfo(teamID, token string) (tm *teams.Team, statusCode int, err error) { | ||||||
| 	return &teams.Team{Members: m.members}, nil | 	return &teams.Team{Members: m.members}, statusCode, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *mockTeamsAPIClient) setMembers(members []string) { | func (m *mockTeamsAPIClient) setMembers(members []string) { | ||||||
|  | @ -260,15 +260,15 @@ type mockTeamsAPIClientMultipleTeams struct { | ||||||
| 	teams []mockTeam | 	teams []mockTeam | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *mockTeamsAPIClientMultipleTeams) TeamInfo(teamID, token string) (tm *teams.Team, err error) { | func (m *mockTeamsAPIClientMultipleTeams) TeamInfo(teamID, token string) (tm *teams.Team, statusCode int, err error) { | ||||||
| 	for _, team := range m.teams { | 	for _, team := range m.teams { | ||||||
| 		if team.teamID == teamID { | 		if team.teamID == teamID { | ||||||
| 			return &teams.Team{Members: team.members}, nil | 			return &teams.Team{Members: team.members}, statusCode, nil | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// should not be reached if a slice with teams is populated correctly
 | 	// should not be reached if a slice with teams is populated correctly
 | ||||||
| 	return nil, nil | 	return nil, statusCode, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Test adding members of maintenance teams that get superuser rights for all PG databases
 | // Test adding members of maintenance teams that get superuser rights for all PG databases
 | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import ( | ||||||
| 	"encoding/gob" | 	"encoding/gob" | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"net/http" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | @ -272,9 +273,14 @@ func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { | ||||||
| 		return nil, fmt.Errorf("could not get oauth token to authenticate to team service API: %v", err) | 		return nil, fmt.Errorf("could not get oauth token to authenticate to team service API: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	teamInfo, err := c.teamsAPIClient.TeamInfo(teamID, token) | 	teamInfo, statusCode, err := c.teamsAPIClient.TeamInfo(teamID, token) | ||||||
|  | 
 | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		c.logger.Warningf("could not get team info for team %q: %v", teamID, err) | 		if statusCode == http.StatusNotFound { | ||||||
|  | 			c.logger.Warningf("could not get team info for team %q: %v", teamID, err) | ||||||
|  | 		} else { | ||||||
|  | 			return nil, fmt.Errorf("could not get team info for team %q: %v", teamID, err) | ||||||
|  | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		for _, member := range teamInfo.Members { | 		for _, member := range teamInfo.Members { | ||||||
| 			if !(util.SliceContains(members, member)) { | 			if !(util.SliceContains(members, member)) { | ||||||
|  |  | ||||||
|  | @ -45,7 +45,7 @@ type httpClient interface { | ||||||
| 
 | 
 | ||||||
| // Interface to the TeamsAPIClient
 | // Interface to the TeamsAPIClient
 | ||||||
| type Interface interface { | type Interface interface { | ||||||
| 	TeamInfo(teamID, token string) (tm *Team, err error) | 	TeamInfo(teamID, token string) (tm *Team, statusCode int, err error) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // API describes teams API
 | // API describes teams API
 | ||||||
|  | @ -67,7 +67,7 @@ func NewTeamsAPI(url string, log *logrus.Entry) *API { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TeamInfo returns information about a given team using its ID and a token to authenticate to the API service.
 | // TeamInfo returns information about a given team using its ID and a token to authenticate to the API service.
 | ||||||
| func (t *API) TeamInfo(teamID, token string) (tm *Team, err error) { | func (t *API) TeamInfo(teamID, token string) (tm *Team, statusCode int, err error) { | ||||||
| 	var ( | 	var ( | ||||||
| 		req  *http.Request | 		req  *http.Request | ||||||
| 		resp *http.Response | 		resp *http.Response | ||||||
|  | @ -77,37 +77,38 @@ func (t *API) TeamInfo(teamID, token string) (tm *Team, err error) { | ||||||
| 	t.logger.Debugf("request url: %s", url) | 	t.logger.Debugf("request url: %s", url) | ||||||
| 	req, err = http.NewRequest("GET", url, nil) | 	req, err = http.NewRequest("GET", url, nil) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, http.StatusBadRequest, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	req.Header.Add("Authorization", "Bearer "+token) | 	req.Header.Add("Authorization", "Bearer "+token) | ||||||
| 	if resp, err = t.httpClient.Do(req); err != nil { | 	if resp, err = t.httpClient.Do(req); err != nil { | ||||||
| 		return nil, err | 		return nil, http.StatusUnauthorized, err | ||||||
| 	} | 	} | ||||||
| 	defer func() { | 	defer func() { | ||||||
| 		if closeErr := resp.Body.Close(); closeErr != nil { | 		if closeErr := resp.Body.Close(); closeErr != nil { | ||||||
| 			err = fmt.Errorf("error when closing response: %v", closeErr) | 			err = fmt.Errorf("error when closing response: %v", closeErr) | ||||||
| 		} | 		} | ||||||
| 	}() | 	}() | ||||||
| 	if resp.StatusCode != 200 { | 	statusCode = resp.StatusCode | ||||||
|  | 	if statusCode != http.StatusOK { | ||||||
| 		var raw map[string]json.RawMessage | 		var raw map[string]json.RawMessage | ||||||
| 		d := json.NewDecoder(resp.Body) | 		d := json.NewDecoder(resp.Body) | ||||||
| 		err = d.Decode(&raw) | 		err = d.Decode(&raw) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, fmt.Errorf("team API query failed with status code %d and malformed response: %v", resp.StatusCode, err) | 			return nil, statusCode, fmt.Errorf("team API query failed with status code %d and malformed response: %v", statusCode, err) | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if errMessage, ok := raw["error"]; ok { | 		if errMessage, ok := raw["error"]; ok { | ||||||
| 			return nil, fmt.Errorf("team API query failed with status code %d and message: '%v'", resp.StatusCode, string(errMessage)) | 			return nil, statusCode, fmt.Errorf("team API query failed with status code %d and message: '%v'", statusCode, string(errMessage)) | ||||||
| 		} | 		} | ||||||
| 		return nil, fmt.Errorf("team API query failed with status code %d", resp.StatusCode) | 		return nil, statusCode, fmt.Errorf("team API query failed with status code %d", statusCode) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	tm = &Team{} | 	tm = &Team{} | ||||||
| 	d := json.NewDecoder(resp.Body) | 	d := json.NewDecoder(resp.Body) | ||||||
| 	if err = d.Decode(tm); err != nil { | 	if err = d.Decode(tm); err != nil { | ||||||
| 		return nil, fmt.Errorf("could not parse team API response: %v", err) | 		return nil, statusCode, fmt.Errorf("could not parse team API response: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return tm, nil | 	return tm, statusCode, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -13,60 +13,63 @@ import ( | ||||||
| var ( | var ( | ||||||
| 	logger = logrus.New().WithField("pkg", "teamsapi") | 	logger = logrus.New().WithField("pkg", "teamsapi") | ||||||
| 	token  = "ec45b1cfbe7100c6315d183a3eb6cec0M2U1LWJkMzEtZDgzNzNmZGQyNGM3IiwiYXV0aF90aW1lIjoxNDkzNzMwNzQ1LCJpc3MiOiJodHRwcz" | 	token  = "ec45b1cfbe7100c6315d183a3eb6cec0M2U1LWJkMzEtZDgzNzNmZGQyNGM3IiwiYXV0aF90aW1lIjoxNDkzNzMwNzQ1LCJpc3MiOiJodHRwcz" | ||||||
|  | 	input  = `{ | ||||||
|  | 	"dn": "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net", | ||||||
|  | 	"id": "acid", | ||||||
|  | 	"id_name": "acid", | ||||||
|  | 	"team_id": "111222", | ||||||
|  | 	"type": "official", | ||||||
|  | 	"name": "Acid team name", | ||||||
|  | 	"mail": [ | ||||||
|  | 	"email1@example.com", | ||||||
|  | 	"email2@example.com" | ||||||
|  | 	], | ||||||
|  | 	"alias": [ | ||||||
|  | 	"acid" | ||||||
|  | 	], | ||||||
|  | 	"member": [ | ||||||
|  | 	  "member1", | ||||||
|  | 	  "member2", | ||||||
|  | 	  "member3" | ||||||
|  | 	], | ||||||
|  | 	"infrastructure-accounts": [ | ||||||
|  | 	{ | ||||||
|  | 	  "id": "1234512345", | ||||||
|  | 	  "name": "acid", | ||||||
|  | 	  "provider": "aws", | ||||||
|  | 	  "type": "aws", | ||||||
|  | 	  "description": "", | ||||||
|  | 	  "owner": "acid", | ||||||
|  | 	  "owner_dn": "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net", | ||||||
|  | 	  "disabled": false | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 	  "id": "5432154321", | ||||||
|  | 	  "name": "db", | ||||||
|  | 	  "provider": "aws", | ||||||
|  | 	  "type": "aws", | ||||||
|  | 	  "description": "", | ||||||
|  | 	  "owner": "acid", | ||||||
|  | 	  "owner_dn": "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net", | ||||||
|  | 	  "disabled": false | ||||||
|  | 	} | ||||||
|  | 	], | ||||||
|  | 	"cost_center": "00099999", | ||||||
|  | 	"delivery_lead": "member4", | ||||||
|  | 	"parent_team_id": "111221" | ||||||
|  | 	}` | ||||||
| ) | ) | ||||||
| 
 |  | ||||||
| var teamsAPItc = []struct { | var teamsAPItc = []struct { | ||||||
| 	in     string | 	in     string | ||||||
| 	inCode int | 	inCode int | ||||||
|  | 	inTeam string | ||||||
| 	out    *Team | 	out    *Team | ||||||
| 	err    error | 	err    error | ||||||
| }{ | }{ | ||||||
| 	{`{ | 	{ | ||||||
| "dn": "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net", | 		input, | ||||||
| "id": "acid", |  | ||||||
| "id_name": "acid", |  | ||||||
| "team_id": "111222", |  | ||||||
| "type": "official", |  | ||||||
| "name": "Acid team name", |  | ||||||
| "mail": [ |  | ||||||
| "email1@example.com", |  | ||||||
| "email2@example.com" |  | ||||||
| ], |  | ||||||
| "alias": [ |  | ||||||
| "acid" |  | ||||||
| ], |  | ||||||
| "member": [ |  | ||||||
|   "member1", |  | ||||||
|   "member2", |  | ||||||
|   "member3" |  | ||||||
| ], |  | ||||||
| "infrastructure-accounts": [ |  | ||||||
| { |  | ||||||
|   "id": "1234512345", |  | ||||||
|   "name": "acid", |  | ||||||
|   "provider": "aws", |  | ||||||
|   "type": "aws", |  | ||||||
|   "description": "", |  | ||||||
|   "owner": "acid", |  | ||||||
|   "owner_dn": "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net", |  | ||||||
|   "disabled": false |  | ||||||
| }, |  | ||||||
| { |  | ||||||
|   "id": "5432154321", |  | ||||||
|   "name": "db", |  | ||||||
|   "provider": "aws", |  | ||||||
|   "type": "aws", |  | ||||||
|   "description": "", |  | ||||||
|   "owner": "acid", |  | ||||||
|   "owner_dn": "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net", |  | ||||||
|   "disabled": false |  | ||||||
| } |  | ||||||
| ], |  | ||||||
| "cost_center": "00099999", |  | ||||||
| "delivery_lead": "member4", |  | ||||||
| "parent_team_id": "111221" |  | ||||||
| }`, |  | ||||||
| 		200, | 		200, | ||||||
|  | 		"acid", | ||||||
| 		&Team{ | 		&Team{ | ||||||
| 			Dn:           "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net", | 			Dn:           "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net", | ||||||
| 			ID:           "acid", | 			ID:           "acid", | ||||||
|  | @ -104,27 +107,38 @@ var teamsAPItc = []struct { | ||||||
| 		nil}, { | 		nil}, { | ||||||
| 		`{"error": "Access Token not valid"}`, | 		`{"error": "Access Token not valid"}`, | ||||||
| 		401, | 		401, | ||||||
|  | 		"acid", | ||||||
| 		nil, | 		nil, | ||||||
| 		fmt.Errorf(`team API query failed with status code 401 and message: '"Access Token not valid"'`), | 		fmt.Errorf(`team API query failed with status code 401 and message: '"Access Token not valid"'`), | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		`{"status": "I'm a teapot'"}`, | 		`{"status": "I'm a teapot'"}`, | ||||||
| 		418, | 		418, | ||||||
|  | 		"acid", | ||||||
| 		nil, | 		nil, | ||||||
| 		fmt.Errorf(`team API query failed with status code 418`), | 		fmt.Errorf(`team API query failed with status code 418`), | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		`{"status": "I'm a teapot`, | 		`{"status": "I'm a teapot`, | ||||||
| 		418, | 		418, | ||||||
|  | 		"acid", | ||||||
| 		nil, | 		nil, | ||||||
| 		fmt.Errorf(`team API query failed with status code 418 and malformed response: unexpected EOF`), | 		fmt.Errorf(`team API query failed with status code 418 and malformed response: unexpected EOF`), | ||||||
| 	}, | 	}, | ||||||
| 	{ | 	{ | ||||||
| 		`{"status": "I'm a teapot`, | 		`{"status": "I'm a teapot`, | ||||||
| 		200, | 		200, | ||||||
|  | 		"acid", | ||||||
| 		nil, | 		nil, | ||||||
| 		fmt.Errorf(`could not parse team API response: unexpected EOF`), | 		fmt.Errorf(`could not parse team API response: unexpected EOF`), | ||||||
| 	}, | 	}, | ||||||
|  | 	{ | ||||||
|  | 		input, | ||||||
|  | 		404, | ||||||
|  | 		"banana", | ||||||
|  | 		nil, | ||||||
|  | 		fmt.Errorf(`team API query failed with status code 404`), | ||||||
|  | 	}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| var requestsURLtc = []struct { | var requestsURLtc = []struct { | ||||||
|  | @ -156,7 +170,7 @@ func TestInfo(t *testing.T) { | ||||||
| 			defer ts.Close() | 			defer ts.Close() | ||||||
| 			api := NewTeamsAPI(ts.URL, logger) | 			api := NewTeamsAPI(ts.URL, logger) | ||||||
| 
 | 
 | ||||||
| 			actual, err := api.TeamInfo("acid", token) | 			actual, statusCode, err := api.TeamInfo(tc.inTeam, token) | ||||||
| 			if err != nil && err.Error() != tc.err.Error() { | 			if err != nil && err.Error() != tc.err.Error() { | ||||||
| 				t.Errorf("expected error: %v, got: %v", tc.err, err) | 				t.Errorf("expected error: %v, got: %v", tc.err, err) | ||||||
| 				return | 				return | ||||||
|  | @ -165,6 +179,10 @@ func TestInfo(t *testing.T) { | ||||||
| 			if !reflect.DeepEqual(actual, tc.out) { | 			if !reflect.DeepEqual(actual, tc.out) { | ||||||
| 				t.Errorf("expected %#v, got: %#v", tc.out, actual) | 				t.Errorf("expected %#v, got: %#v", tc.out, actual) | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			if statusCode != tc.inCode { | ||||||
|  | 				t.Errorf("expected %d, got: %d", tc.inCode, statusCode) | ||||||
|  | 			} | ||||||
| 		}() | 		}() | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -202,7 +220,7 @@ func TestHttpClientClose(t *testing.T) { | ||||||
| 	api := NewTeamsAPI(ts.URL, logger) | 	api := NewTeamsAPI(ts.URL, logger) | ||||||
| 	api.httpClient = &mockHTTPClient{} | 	api.httpClient = &mockHTTPClient{} | ||||||
| 
 | 
 | ||||||
| 	_, err := api.TeamInfo("acid", token) | 	_, _, err := api.TeamInfo("acid", token) | ||||||
| 	expError := fmt.Errorf("error when closing response: close error") | 	expError := fmt.Errorf("error when closing response: close error") | ||||||
| 	if err.Error() != expError.Error() { | 	if err.Error() != expError.Error() { | ||||||
| 		t.Errorf("expected error: %v, got: %v", expError, err) | 		t.Errorf("expected error: %v, got: %v", expError, err) | ||||||
|  | @ -212,7 +230,7 @@ func TestHttpClientClose(t *testing.T) { | ||||||
| func TestRequest(t *testing.T) { | func TestRequest(t *testing.T) { | ||||||
| 	for _, tc := range requestsURLtc { | 	for _, tc := range requestsURLtc { | ||||||
| 		api := NewTeamsAPI(tc.url, logger) | 		api := NewTeamsAPI(tc.url, logger) | ||||||
| 		resp, err := api.TeamInfo("acid", token) | 		resp, _, err := api.TeamInfo("acid", token) | ||||||
| 		if resp != nil { | 		if resp != nil { | ||||||
| 			t.Errorf("response expected to be nil") | 			t.Errorf("response expected to be nil") | ||||||
| 			continue | 			continue | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue