From c7d571889cd341a480a7de7fcb6f46d4ee767238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20M=C3=B6hlmann?= Date: Tue, 14 Nov 2023 17:23:06 +0200 Subject: [PATCH] query userinfo unit test --- internal/query/testdata/userinfo_human.json | 45 ++++ .../query/testdata/userinfo_human_no_md.json | 28 +++ internal/query/testdata/userinfo_machine.json | 38 ++++ .../query/testdata/userinfo_not_found.json | 5 + internal/query/userinfo.go | 28 +-- internal/query/userinfo_test.go | 215 ++++++++++++++++++ 6 files changed, 346 insertions(+), 13 deletions(-) create mode 100644 internal/query/testdata/userinfo_human.json create mode 100644 internal/query/testdata/userinfo_human_no_md.json create mode 100644 internal/query/testdata/userinfo_machine.json create mode 100644 internal/query/testdata/userinfo_not_found.json create mode 100644 internal/query/userinfo_test.go diff --git a/internal/query/testdata/userinfo_human.json b/internal/query/testdata/userinfo_human.json new file mode 100644 index 00000000000..45fb80b1cc5 --- /dev/null +++ b/internal/query/testdata/userinfo_human.json @@ -0,0 +1,45 @@ +{ + "user": { + "id": "231965491734773762", + "creation_date": "2023-09-15T06:10:07.434142+00:00", + "change_date": "2023-11-14T13:27:02.072318+00:00", + "sequence": 1148, + "state": 1, + "resource_owner": "231848297847848962", + "username": "tim+tesmail@zitadel.com", + "human": { + "first_name": "Tim", + "last_name": "Mohlmann", + "nick_name": "muhlemmer", + "display_name": "Tim Mohlmann", + "avatar_key": null, + "email": "tim+tesmail@zitadel.com", + "is_email_verified": true, + "phone": "+40123456789", + "is_phone_verified": false + }, + "machine": null + }, + "org": { + "name": "demo", + "primary_domain": "demo.localhost" + }, + "metadata": [ + { + "creation_date": "2023-11-14T13:26:03.553702+00:00", + "change_date": "2023-11-14T13:26:03.553702+00:00", + "sequence": 1147, + "resource_owner": "231848297847848962", + "key": "bar", + "value": "Zm9v" + }, + { + "creation_date": "2023-11-14T13:25:57.171368+00:00", + "change_date": "2023-11-14T13:25:57.171368+00:00", + "sequence": 1146, + "resource_owner": "231848297847848962", + "key": "foo", + "value": "YmFy" + } + ] + } diff --git a/internal/query/testdata/userinfo_human_no_md.json b/internal/query/testdata/userinfo_human_no_md.json new file mode 100644 index 00000000000..162d34ff278 --- /dev/null +++ b/internal/query/testdata/userinfo_human_no_md.json @@ -0,0 +1,28 @@ +{ + "user": { + "id": "231965491734773762", + "creation_date": "2023-09-15T06:10:07.434142+00:00", + "change_date": "2023-11-14T13:27:02.072318+00:00", + "sequence": 1148, + "state": 1, + "resource_owner": "231848297847848962", + "username": "tim+tesmail@zitadel.com", + "human": { + "first_name": "Tim", + "last_name": "Mohlmann", + "nick_name": "muhlemmer", + "display_name": "Tim Mohlmann", + "avatar_key": null, + "email": "tim+tesmail@zitadel.com", + "is_email_verified": true, + "phone": "+40123456789", + "is_phone_verified": false + }, + "machine": null + }, + "org": { + "name": "demo", + "primary_domain": "demo.localhost" + }, + "metadata": null + } diff --git a/internal/query/testdata/userinfo_machine.json b/internal/query/testdata/userinfo_machine.json new file mode 100644 index 00000000000..b5e2cc1623e --- /dev/null +++ b/internal/query/testdata/userinfo_machine.json @@ -0,0 +1,38 @@ +{ + "user": { + "id": "240707570677841922", + "creation_date": "2023-11-14T13:34:52.473732+00:00", + "change_date": "2023-11-14T13:35:02.861342+00:00", + "sequence": 2, + "state": 1, + "resource_owner": "231848297847848962", + "username": "tests", + "human": null, + "machine": { + "name": "tests", + "description": "My test service user" + } + }, + "org": { + "name": "demo", + "primary_domain": "demo.localhost" + }, + "metadata": [ + { + "creation_date": "2023-11-14T13:35:30.126849+00:00", + "change_date": "2023-11-14T13:35:30.126849+00:00", + "sequence": 3, + "resource_owner": "231848297847848962", + "key": "first", + "value": "SGVsbG8gV29ybGQh" + }, + { + "creation_date": "2023-11-14T13:35:44.028343+00:00", + "change_date": "2023-11-14T13:35:44.028343+00:00", + "sequence": 4, + "resource_owner": "231848297847848962", + "key": "second", + "value": "QnllIFdvcmxkIQ==" + } + ] +} diff --git a/internal/query/testdata/userinfo_not_found.json b/internal/query/testdata/userinfo_not_found.json new file mode 100644 index 00000000000..d2757643d79 --- /dev/null +++ b/internal/query/testdata/userinfo_not_found.json @@ -0,0 +1,5 @@ +{ + "user": null, + "org": null, + "metadata": null +} diff --git a/internal/query/userinfo.go b/internal/query/userinfo.go index 6acf1ed4b9a..e6c8804b65f 100644 --- a/internal/query/userinfo.go +++ b/internal/query/userinfo.go @@ -5,7 +5,6 @@ import ( "database/sql" _ "embed" "encoding/json" - "fmt" "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/errors" @@ -19,16 +18,17 @@ func (q *Queries) GetOIDCUserInfo(ctx context.Context, userID string) (_ *OIDCUs ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() - userInfo := new(OIDCUserInfo) + var data []byte err = q.client.QueryRowContext(ctx, func(row *sql.Row) error { - var data []byte - if err := row.Scan(&data); err != nil { - return err - } - return json.Unmarshal(data, userInfo) + return row.Scan(&data) }, oidcUserInfoQuery, userID, authz.GetInstance(ctx).InstanceID()) if err != nil { - return nil, fmt.Errorf("get oidc user info: %w", err) + return nil, errors.ThrowInternal(err, "QUERY-Oath6", "Errors.Internal") + } + + userInfo := new(OIDCUserInfo) + if err = json.Unmarshal(data, userInfo); err != nil { + return nil, errors.ThrowInternal(err, "QUERY-Vohs6", "Errors.Internal") } if userInfo.User == nil { return nil, errors.ThrowNotFound(nil, "QUERY-ahs4S", "Errors.User.NotFound") @@ -40,9 +40,11 @@ func (q *Queries) GetOIDCUserInfo(ctx context.Context, userID string) (_ *OIDCUs type OIDCUserInfo struct { User *User `json:"user,omitempty"` Metadata []UserMetadata `json:"metadata,omitempty"` - Org *struct { - ID string `json:"id,omitempty"` - Name string `json:"name,omitempty"` - PrimaryDomain string `json:"primary_domain,omitempty"` - } `json:"org,omitempty"` + Org *userInfoOrg `json:"org,omitempty"` +} + +type userInfoOrg struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + PrimaryDomain string `json:"primary_domain,omitempty"` } diff --git a/internal/query/userinfo_test.go b/internal/query/userinfo_test.go new file mode 100644 index 00000000000..1e97186b72b --- /dev/null +++ b/internal/query/userinfo_test.go @@ -0,0 +1,215 @@ +package query + +import ( + "database/sql" + "database/sql/driver" + _ "embed" + "regexp" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/errors" +) + +var ( + //go:embed testdata/userinfo_not_found.json + testdataUserInfoNotFound string + //go:embed testdata/userinfo_human_no_md.json + testdataUserInfoHumanNoMD string + //go:embed testdata/userinfo_human.json + testdataUserInfoHuman string + //go:embed testdata/userinfo_machine.json + testdataUserInfoMachine string +) + +func TestQueries_GetOIDCUserInfo(t *testing.T) { + expQuery := regexp.QuoteMeta(oidcUserInfoQuery) + type args struct { + userID string + } + tests := []struct { + name string + args args + mock sqlExpectation + want *OIDCUserInfo + wantErr error + }{ + { + name: "query error", + args: args{ + userID: "231965491734773762", + }, + mock: mockQueryErr(expQuery, sql.ErrConnDone, "231965491734773762", "instanceID"), + wantErr: sql.ErrConnDone, + }, + { + name: "unmarshal error", + args: args{ + userID: "231965491734773762", + }, + mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{`~~~`}, "231965491734773762", "instanceID"), + wantErr: errors.ThrowInternal(nil, "QUERY-Vohs6", "Errors.Internal"), + }, + { + name: "user not found", + args: args{ + userID: "231965491734773762", + }, + mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoNotFound}, "231965491734773762", "instanceID"), + wantErr: errors.ThrowNotFound(nil, "QUERY-ahs4S", "Errors.User.NotFound"), + }, + { + name: "human without metadata", + args: args{ + userID: "231965491734773762", + }, + mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoHumanNoMD}, "231965491734773762", "instanceID"), + want: &OIDCUserInfo{ + User: &User{ + ID: "231965491734773762", + CreationDate: time.Date(2023, time.September, 15, 6, 10, 7, 434142000, time.FixedZone("", 0)), + ChangeDate: time.Date(2023, time.November, 14, 13, 27, 2, 72318000, time.FixedZone("", 0)), + Sequence: 1148, + State: 1, + ResourceOwner: "231848297847848962", + Username: "tim+tesmail@zitadel.com", + Human: &Human{ + FirstName: "Tim", + LastName: "Mohlmann", + NickName: "muhlemmer", + DisplayName: "Tim Mohlmann", + AvatarKey: "", + Email: "tim+tesmail@zitadel.com", + IsEmailVerified: true, + Phone: "+40123456789", + IsPhoneVerified: false, + }, + Machine: nil, + }, + Org: &userInfoOrg{ + Name: "demo", + PrimaryDomain: "demo.localhost", + }, + Metadata: nil, + }, + }, + { + name: "human with metadata", + args: args{ + userID: "231965491734773762", + }, + mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoHuman}, "231965491734773762", "instanceID"), + want: &OIDCUserInfo{ + User: &User{ + ID: "231965491734773762", + CreationDate: time.Date(2023, time.September, 15, 6, 10, 7, 434142000, time.FixedZone("", 0)), + ChangeDate: time.Date(2023, time.November, 14, 13, 27, 2, 72318000, time.FixedZone("", 0)), + Sequence: 1148, + State: 1, + ResourceOwner: "231848297847848962", + Username: "tim+tesmail@zitadel.com", + Human: &Human{ + FirstName: "Tim", + LastName: "Mohlmann", + NickName: "muhlemmer", + DisplayName: "Tim Mohlmann", + AvatarKey: "", + Email: "tim+tesmail@zitadel.com", + IsEmailVerified: true, + Phone: "+40123456789", + IsPhoneVerified: false, + }, + Machine: nil, + }, + Org: &userInfoOrg{ + Name: "demo", + PrimaryDomain: "demo.localhost", + }, + Metadata: []UserMetadata{ + { + CreationDate: time.Date(2023, time.November, 14, 13, 26, 3, 553702000, time.FixedZone("", 0)), + ChangeDate: time.Date(2023, time.November, 14, 13, 26, 3, 553702000, time.FixedZone("", 0)), + Sequence: 1147, + ResourceOwner: "231848297847848962", + Key: "bar", + Value: []byte("foo"), + }, + { + CreationDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, time.FixedZone("", 0)), + ChangeDate: time.Date(2023, time.November, 14, 13, 25, 57, 171368000, time.FixedZone("", 0)), + Sequence: 1146, + ResourceOwner: "231848297847848962", + Key: "foo", + Value: []byte("bar"), + }, + }, + }, + }, + { + name: "machine with metadata", + args: args{ + userID: "240707570677841922", + }, + mock: mockQuery(expQuery, []string{"json_build_object"}, []driver.Value{testdataUserInfoMachine}, "240707570677841922", "instanceID"), + want: &OIDCUserInfo{ + User: &User{ + ID: "240707570677841922", + CreationDate: time.Date(2023, time.November, 14, 13, 34, 52, 473732000, time.FixedZone("", 0)), + ChangeDate: time.Date(2023, time.November, 14, 13, 35, 2, 861342000, time.FixedZone("", 0)), + Sequence: 2, + State: 1, + ResourceOwner: "231848297847848962", + Username: "tests", + Human: nil, + Machine: &Machine{ + Name: "tests", + Description: "My test service user", + }, + }, + Org: &userInfoOrg{ + Name: "demo", + PrimaryDomain: "demo.localhost", + }, + Metadata: []UserMetadata{ + { + CreationDate: time.Date(2023, time.November, 14, 13, 35, 30, 126849000, time.FixedZone("", 0)), + ChangeDate: time.Date(2023, time.November, 14, 13, 35, 30, 126849000, time.FixedZone("", 0)), + Sequence: 3, + ResourceOwner: "231848297847848962", + Key: "first", + Value: []byte("Hello World!"), + }, + { + CreationDate: time.Date(2023, time.November, 14, 13, 35, 44, 28343000, time.FixedZone("", 0)), + ChangeDate: time.Date(2023, time.November, 14, 13, 35, 44, 28343000, time.FixedZone("", 0)), + Sequence: 4, + ResourceOwner: "231848297847848962", + Key: "second", + Value: []byte("Bye World!"), + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + execMock(t, tt.mock, func(db *sql.DB) { + q := &Queries{ + client: &database.DB{ + DB: db, + Database: &prepareDB{}, + }, + } + ctx := authz.NewMockContext("instanceID", "orgID", "loginClient") + + got, err := q.GetOIDCUserInfo(ctx, tt.args.userID) + require.ErrorIs(t, err, tt.wantErr) + assert.Equal(t, tt.want, got) + }) + }) + } +}