package services_test import ( "context" "crypto/rand" "crypto/rsa" "crypto/sha256" "crypto/x509" "encoding/base64" "encoding/pem" "os" "regexp" "testing" "time" "github.com/DATA-DOG/go-sqlmock" "github.com/rede5/gohorsejobs/backend/internal/services" "github.com/stretchr/testify/assert" ) // Helper to generate a valid RSA private key for testing func generateTestRSAKey() (string, *rsa.PublicKey, error) { key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return "", nil, err } keyBytes := x509.MarshalPKCS1PrivateKey(key) pemBlock := &pem.Block{ Type: "RSA PRIVATE KEY", Bytes: keyBytes, } pemBytes := pem.EncodeToMemory(pemBlock) return base64.StdEncoding.EncodeToString(pemBytes), &key.PublicKey, nil } func TestSaveCredentials(t *testing.T) { db, mock, err := sqlmock.New() assert.NoError(t, err) defer db.Close() service := services.NewCredentialsService(db) ctx := context.Background() mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO external_services_credentials`)). WithArgs("stripe", "encrypted_data", "admin"). WillReturnResult(sqlmock.NewResult(1, 1)) err = service.SaveCredentials(ctx, "stripe", "encrypted_data", "admin") assert.NoError(t, err) } func TestGetDecryptedKey(t *testing.T) { // Setup RSA Key privKeyStr, pubKey, err := generateTestRSAKey() assert.NoError(t, err) os.Setenv("RSA_PRIVATE_KEY_BASE64", privKeyStr) defer os.Unsetenv("RSA_PRIVATE_KEY_BASE64") db, mock, err := sqlmock.New() assert.NoError(t, err) defer db.Close() service := services.NewCredentialsService(db) ctx := context.Background() // Encrypt a secret secret := "my-secret-key" encryptedBytes, err := rsa.EncryptOAEP( sha256.New(), rand.Reader, pubKey, []byte(secret), nil, ) assert.NoError(t, err) encryptedPayload := base64.StdEncoding.EncodeToString(encryptedBytes) // Mock DB return mock.ExpectQuery(regexp.QuoteMeta(`SELECT encrypted_payload FROM external_services_credentials`)). WithArgs("stripe"). WillReturnRows(sqlmock.NewRows([]string{"encrypted_payload"}).AddRow(encryptedPayload)) // Execute decrypted, err := service.GetDecryptedKey(ctx, "stripe") assert.NoError(t, err) assert.Equal(t, secret, decrypted) } func TestListConfiguredServices(t *testing.T) { db, mock, err := sqlmock.New() assert.NoError(t, err) defer db.Close() service := services.NewCredentialsService(db) ctx := context.Background() mock.ExpectQuery(regexp.QuoteMeta(`SELECT service_name, updated_at, COALESCE(updated_by::text, '')`)). WillReturnRows(sqlmock.NewRows([]string{"service_name", "updated_at", "updated_by"}). AddRow("stripe", time.Now().Format(time.RFC3339), "admin"). AddRow("appwrite", time.Now().Format(time.RFC3339), "system")) servicesList, err := service.ListConfiguredServices(ctx) assert.NoError(t, err) assert.NotEmpty(t, servicesList) // Verify mapped correctly foundStripe := false for _, s := range servicesList { if s.ServiceName == "stripe" { assert.True(t, s.IsConfigured) foundStripe = true } if s.ServiceName == "firebase" { assert.False(t, s.IsConfigured) } } assert.True(t, foundStripe) } func TestDeleteCredentials(t *testing.T) { db, mock, err := sqlmock.New() assert.NoError(t, err) defer db.Close() service := services.NewCredentialsService(db) ctx := context.Background() mock.ExpectExec(regexp.QuoteMeta(`DELETE FROM external_services_credentials`)). WithArgs("stripe"). WillReturnResult(sqlmock.NewResult(1, 1)) err = service.DeleteCredentials(ctx, "stripe") assert.NoError(t, err) } func TestEncryptPayload(t *testing.T) { // Setup RSA Key privKeyStr, _, err := generateTestRSAKey() assert.NoError(t, err) os.Setenv("RSA_PRIVATE_KEY_BASE64", privKeyStr) defer os.Unsetenv("RSA_PRIVATE_KEY_BASE64") service := services.NewCredentialsService(nil) // Encrypt payload := "test-payload-123" encrypted, err := service.EncryptPayload(payload) assert.NoError(t, err) assert.NotEmpty(t, encrypted) assert.NotEqual(t, payload, encrypted) }