/
githubmirror
/
grafana
Обзор
Документация
Войти
/
githubmirror
/
grafana
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
apps/provisioning/pkg/connection/github/extra_test.go
374 строки
10 KB
Alejandro
Provisioning: (2/8) Add shared OAuth app connection schema (#129739)
31 июл 2026, 17:27
Не верифицирован
31 июл 2026, 17:27
0e497b9
Код
Авторство
О чём код?
package github_test import ( "context" "encoding/base64" "errors" "testing" "k8s.io/apimachinery/pkg/runtime" provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1" "github.com/grafana/grafana/apps/provisioning/pkg/connection" "github.com/grafana/grafana/apps/provisioning/pkg/connection/github" common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type mockSecureValues struct { privateKey common.RawSecureValue privateKeyErr error clientSecret common.RawSecureValue clientSecErr error token common.RawSecureValue tokenErr error } func (m *mockSecureValues) PrivateKey(_ context.Context) (common.RawSecureValue, error) { return m.privateKey, m.privateKeyErr } func (m *mockSecureValues) ClientSecret(_ context.Context) (common.RawSecureValue, error) { return m.clientSecret, m.clientSecErr } func (m *mockSecureValues) Token(_ context.Context) (common.RawSecureValue, error) { return m.token, m.tokenErr } func TestExtra_Type(t *testing.T) { mockFactory := github.NewMockGithubFactory(t) decrypter := func(c *provisioning.Connection) connection.SecureValues { return &mockSecureValues{} } e := github.Extra(decrypter, mockFactory) result := e.Type() assert.Equal(t, provisioning.GithubConnectionType, result) } func TestExtra_Build(t *testing.T) { tests := []struct { name string conn *provisioning.Connection setupDecrypter func() connection.Decrypter expectedError string }{ { name: "success with valid connection", conn: &provisioning.Connection{ ObjectMeta: metav1.ObjectMeta{ Name: "test-connection", Namespace: "default", }, Spec: provisioning.ConnectionSpec{ Type: provisioning.GithubConnectionType, GitHub: &provisioning.GitHubConnectionConfig{ AppID: "123456", InstallationID: "789012", }, }, }, setupDecrypter: func() connection.Decrypter { return func(c *provisioning.Connection) connection.SecureValues { return &mockSecureValues{ privateKey: common.RawSecureValue("test-private-key"), token: common.RawSecureValue("test-token"), } } }, }, { name: "nil connection", conn: nil, setupDecrypter: func() connection.Decrypter { return func(c *provisioning.Connection) connection.SecureValues { return &mockSecureValues{} } }, expectedError: "invalid github connection", }, { name: "connection without github config", conn: &provisioning.Connection{ ObjectMeta: metav1.ObjectMeta{ Name: "test-connection", Namespace: "default", }, Spec: provisioning.ConnectionSpec{ Type: provisioning.GithubConnectionType, GitHub: nil, }, }, setupDecrypter: func() connection.Decrypter { return func(c *provisioning.Connection) connection.SecureValues { return &mockSecureValues{} } }, expectedError: "invalid github connection", }, { name: "error decrypting private key", conn: &provisioning.Connection{ ObjectMeta: metav1.ObjectMeta{ Name: "test-connection", Namespace: "default", }, Spec: provisioning.ConnectionSpec{ Type: provisioning.GithubConnectionType, GitHub: &provisioning.GitHubConnectionConfig{ AppID: "123456", InstallationID: "789012", }, }, }, setupDecrypter: func() connection.Decrypter { return func(c *provisioning.Connection) connection.SecureValues { return &mockSecureValues{ privateKeyErr: errors.New("failed to decrypt private key"), } } }, expectedError: "failed to decrypt private key", }, { name: "error decrypting token", conn: &provisioning.Connection{ ObjectMeta: metav1.ObjectMeta{ Name: "test-connection", Namespace: "default", }, Spec: provisioning.ConnectionSpec{ Type: provisioning.GithubConnectionType, GitHub: &provisioning.GitHubConnectionConfig{ AppID: "123456", InstallationID: "789012", }, }, }, setupDecrypter: func() connection.Decrypter { return func(c *provisioning.Connection) connection.SecureValues { return &mockSecureValues{ privateKey: common.RawSecureValue("test-private-key"), tokenErr: errors.New("failed to decrypt token"), } } }, expectedError: "failed to decrypt token", }, { name: "success with empty secure values", conn: &provisioning.Connection{ ObjectMeta: metav1.ObjectMeta{ Name: "test-connection", Namespace: "default", }, Spec: provisioning.ConnectionSpec{ Type: provisioning.GithubConnectionType, GitHub: &provisioning.GitHubConnectionConfig{ AppID: "123456", InstallationID: "789012", }, }, }, setupDecrypter: func() connection.Decrypter { return func(c *provisioning.Connection) connection.SecureValues { return &mockSecureValues{ privateKey: common.RawSecureValue(""), token: common.RawSecureValue(""), } } }, }, { name: "success with different app and installation IDs", conn: &provisioning.Connection{ ObjectMeta: metav1.ObjectMeta{ Name: "another-connection", Namespace: "prod", }, Spec: provisioning.ConnectionSpec{ Type: provisioning.GithubConnectionType, GitHub: &provisioning.GitHubConnectionConfig{ AppID: "999888", InstallationID: "777666", }, }, }, setupDecrypter: func() connection.Decrypter { return func(c *provisioning.Connection) connection.SecureValues { return &mockSecureValues{ privateKey: common.RawSecureValue("another-private-key"), token: common.RawSecureValue("another-token"), } } }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := t.Context() mockFactory := github.NewMockGithubFactory(t) decrypter := tt.setupDecrypter() e := github.Extra(decrypter, mockFactory) result, err := e.Build(ctx, tt.conn) if tt.expectedError != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.expectedError) assert.Nil(t, result) } else { require.NoError(t, err) assert.NotNil(t, result) } }) } } func TestExtra_Mutate(t *testing.T) { privateKeyBase64 := base64.StdEncoding.EncodeToString([]byte(testPrivateKeyPEM)) tests := []struct { name string obj runtime.Object wantErr bool validateError func(t *testing.T, err error) validateResult func(t *testing.T, obj runtime.Object) }{ { name: "should successfully mutate GitHub connection without generating token", obj: &provisioning.Connection{ ObjectMeta: metav1.ObjectMeta{Name: "test-connection"}, Spec: provisioning.ConnectionSpec{ Type: provisioning.GithubConnectionType, GitHub: &provisioning.GitHubConnectionConfig{ AppID: "123", InstallationID: "456", }, }, Secure: provisioning.ConnectionSecure{ PrivateKey: common.InlineSecureValue{ Create: common.NewSecretValue(privateKeyBase64), }, }, }, wantErr: false, validateResult: func(t *testing.T, obj runtime.Object) { conn := obj.(*provisioning.Connection) assert.Equal(t, "https://github.com/settings/installations/456", conn.Spec.URL) assert.True(t, conn.Secure.Token.Create.IsZero(), "Token should not be generated in mutator") }, }, { name: "should be no-op for non-Connection objects", obj: &provisioning.Repository{ ObjectMeta: metav1.ObjectMeta{Name: "test-repo"}, }, wantErr: false, validateResult: func(t *testing.T, obj runtime.Object) { // Object should remain unchanged repo := obj.(*provisioning.Repository) assert.Equal(t, "test-repo", repo.Name) }, }, { name: "should be no-op for Connection without GitHub config", obj: &provisioning.Connection{ ObjectMeta: metav1.ObjectMeta{Name: "test-connection"}, Spec: provisioning.ConnectionSpec{ Type: provisioning.GitlabConnectionType, OAuth: &provisioning.ConnectionOAuthConfig{ ClientID: "clientID", }, }, }, wantErr: false, validateResult: func(t *testing.T, obj runtime.Object) { conn := obj.(*provisioning.Connection) assert.Empty(t, conn.Spec.URL, "URL should not be set") assert.True(t, conn.Secure.Token.Create.IsZero(), "Token should not be generated") }, }, { name: "should generate URL from installation ID", obj: &provisioning.Connection{ ObjectMeta: metav1.ObjectMeta{Name: "test-connection"}, Spec: provisioning.ConnectionSpec{ Type: provisioning.GithubConnectionType, GitHub: &provisioning.GitHubConnectionConfig{ AppID: "789", InstallationID: "012", }, }, }, wantErr: false, validateResult: func(t *testing.T, obj runtime.Object) { conn := obj.(*provisioning.Connection) assert.Equal(t, "https://github.com/settings/installations/012", conn.Spec.URL) }, }, { name: "should preserve token when no new key provided", obj: &provisioning.Connection{ ObjectMeta: metav1.ObjectMeta{Name: "test-connection"}, Spec: provisioning.ConnectionSpec{ Type: provisioning.GithubConnectionType, GitHub: &provisioning.GitHubConnectionConfig{ AppID: "123", InstallationID: "456", }, }, Secure: provisioning.ConnectionSecure{ PrivateKey: common.InlineSecureValue{ Name: "existing-key", }, Token: common.InlineSecureValue{ Create: common.NewSecretValue("existing-token"), }, }, }, wantErr: false, validateResult: func(t *testing.T, obj runtime.Object) { conn := obj.(*provisioning.Connection) assert.Equal(t, "https://github.com/settings/installations/456", conn.Spec.URL) // Token should remain unchanged assert.Equal(t, "existing-token", conn.Secure.Token.Create.DangerouslyExposeAndConsumeValue()) }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := t.Context() mockFactory := github.NewMockGithubFactory(t) decrypter := func(c *provisioning.Connection) connection.SecureValues { return &mockSecureValues{} } e := github.Extra(decrypter, mockFactory) err := e.Mutate(ctx, tt.obj) if tt.wantErr { require.Error(t, err) if tt.validateError != nil { tt.validateError(t, err) } } else { require.NoError(t, err) if tt.validateResult != nil { tt.validateResult(t, tt.obj) } } }) } }