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:
Jociele Padilha 2022-04-14 10:02:54 +02:00 committed by GitHub
parent 9bcb25ac7e
commit 483bf624ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 267 additions and 242 deletions

View File

@ -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

View File

@ -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)) {

View File

@ -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
} }

View File

@ -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