-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: provision a new tenant through CLI
- Loading branch information
1 parent
dd235f0
commit 7b13b6c
Showing
10 changed files
with
686 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,9 +4,11 @@ import ( | |
"context" | ||
"errors" | ||
"fmt" | ||
"net/url" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/lib/pq" | ||
"github.com/spf13/cobra" | ||
"github.com/stellar/go/support/log" | ||
"github.com/stellar/stellar-disbursement-platform-backend/db" | ||
|
@@ -17,9 +19,20 @@ import ( | |
) | ||
|
||
func DeleteAllTenantsFixture(t *testing.T, ctx context.Context, dbConnectionPool db.DBConnectionPool) { | ||
const q = "DELETE FROM tenants" | ||
q := "DELETE FROM tenants" | ||
_, err := dbConnectionPool.ExecContext(ctx, q) | ||
require.NoError(t, err) | ||
|
||
var schemasToDrop []string | ||
q = "SELECT schema_name FROM information_schema.schemata WHERE schema_name ILIKE 'sdp_%'" | ||
err = dbConnectionPool.SelectContext(ctx, &schemasToDrop, q) | ||
require.NoError(t, err) | ||
|
||
for _, schema := range schemasToDrop { | ||
q = fmt.Sprintf("DROP SCHEMA %s CASCADE", pq.QuoteIdentifier(schema)) | ||
_, err = dbConnectionPool.ExecContext(ctx, q) | ||
require.NoError(t, err) | ||
} | ||
} | ||
|
||
func Test_validateTenantNameArg(t *testing.T) { | ||
|
@@ -81,7 +94,7 @@ func Test_executeAddTenant(t *testing.T) { | |
|
||
getEntries := log.DefaultLogger.StartTest(log.InfoLevel) | ||
|
||
err := executeAddTenant(ctx, dbt.DSN, "myorg") | ||
err := executeAddTenant(ctx, dbt.DSN, "myorg", "first", "last", "[email protected]", "testnet") | ||
assert.Nil(t, err) | ||
|
||
const q = "SELECT id FROM tenants WHERE name = $1" | ||
|
@@ -90,20 +103,20 @@ func Test_executeAddTenant(t *testing.T) { | |
require.NoError(t, err) | ||
|
||
entries := getEntries() | ||
require.Len(t, entries, 2) | ||
assert.Equal(t, "tenant myorg added successfully", entries[0].Message) | ||
assert.Contains(t, fmt.Sprintf("tenant ID: %s", tenantID), entries[1].Message) | ||
require.Len(t, entries, 15) | ||
assert.Equal(t, "tenant myorg added successfully", entries[13].Message) | ||
assert.Contains(t, fmt.Sprintf("tenant ID: %s", tenantID), entries[14].Message) | ||
}) | ||
|
||
t.Run("duplicated tenant name", func(t *testing.T) { | ||
DeleteAllTenantsFixture(t, ctx, dbConnectionPool) | ||
|
||
getEntries := log.DefaultLogger.StartTest(log.DebugLevel) | ||
|
||
err := executeAddTenant(ctx, dbt.DSN, "myorg") | ||
err := executeAddTenant(ctx, dbt.DSN, "myorg", "first", "last", "[email protected]", "testnet") | ||
assert.Nil(t, err) | ||
|
||
err = executeAddTenant(ctx, dbt.DSN, "MyOrg") | ||
err = executeAddTenant(ctx, dbt.DSN, "myorg", "first", "last", "[email protected]", "testnet") | ||
assert.ErrorIs(t, err, tenant.ErrDuplicatedTenantName) | ||
|
||
const q = "SELECT id FROM tenants WHERE name = $1" | ||
|
@@ -112,9 +125,9 @@ func Test_executeAddTenant(t *testing.T) { | |
require.NoError(t, err) | ||
|
||
entries := getEntries() | ||
require.Len(t, entries, 2) | ||
assert.Equal(t, "tenant myorg added successfully", entries[0].Message) | ||
assert.Contains(t, fmt.Sprintf("tenant ID: %s", tenantID), entries[1].Message) | ||
require.Len(t, entries, 16) | ||
assert.Equal(t, "tenant myorg added successfully", entries[13].Message) | ||
assert.Contains(t, fmt.Sprintf("tenant ID: %s", tenantID), entries[14].Message) | ||
}) | ||
} | ||
|
||
|
@@ -135,17 +148,18 @@ func Test_AddTenantsCmd(t *testing.T) { | |
mockCmd.SetErr(out) | ||
mockCmd.SetArgs([]string{"add-tenants"}) | ||
err := mockCmd.ExecuteContext(ctx) | ||
assert.EqualError(t, err, "accepts 1 arg(s), received 0") | ||
assert.EqualError(t, err, "accepts 4 arg(s), received 0") | ||
|
||
expectUsageMessage := `Error: accepts 1 arg(s), received 0 | ||
expectUsageMessage := `Error: accepts 4 arg(s), received 0 | ||
Usage: | ||
add-tenants [flags] | ||
Examples: | ||
add-tenants [name] | ||
add-tenants [tenant name] [user first name] [user last name] [user email] | ||
Flags: | ||
-h, --help help for add-tenants | ||
-h, --help help for add-tenants | ||
--network-type string (NETWORK_TYPE) (default "testnet") | ||
` | ||
assert.Equal(t, expectUsageMessage, out.String()) | ||
|
@@ -161,21 +175,27 @@ Usage: | |
add-tenants [flags] | ||
Examples: | ||
add-tenants [name] | ||
add-tenants [tenant name] [user first name] [user last name] [user email] | ||
Flags: | ||
-h, --help help for add-tenants | ||
-h, --help help for add-tenants | ||
--network-type string (NETWORK_TYPE) (default "testnet") | ||
` | ||
assert.Equal(t, expectUsageMessage, out.String()) | ||
}) | ||
|
||
t.Run("adds new tenant successfully", func(t *testing.T) { | ||
t.Run("adds new tenant successfully testnet", func(t *testing.T) { | ||
tenantName := "unhcr" | ||
userFirstName := "First" | ||
userLastName := "Last" | ||
userEmail := "[email protected]" | ||
|
||
out := new(strings.Builder) | ||
rootCmd := rootCmd() | ||
rootCmd.AddCommand(AddTenantsCmd()) | ||
rootCmd.SetOut(out) | ||
rootCmd.SetErr(out) | ||
rootCmd.SetArgs([]string{"add-tenants", "unhcr", "--multitenant-db-url", dbt.DSN}) | ||
rootCmd.SetArgs([]string{"add-tenants", tenantName, userFirstName, userLastName, userEmail, "--network-type", "testnet", "--multitenant-db-url", dbt.DSN}) | ||
getEntries := log.DefaultLogger.StartTest(log.InfoLevel) | ||
|
||
err := rootCmd.ExecuteContext(ctx) | ||
|
@@ -184,12 +204,117 @@ Flags: | |
|
||
const q = "SELECT id FROM tenants WHERE name = $1" | ||
var tenantID string | ||
err = dbConnectionPool.GetContext(ctx, &tenantID, q, "unhcr") | ||
err = dbConnectionPool.GetContext(ctx, &tenantID, q, tenantName) | ||
require.NoError(t, err) | ||
|
||
entries := getEntries() | ||
require.Len(t, entries, 4) | ||
assert.Equal(t, "tenant unhcr added successfully", entries[2].Message) | ||
assert.Contains(t, fmt.Sprintf("tenant ID: %s", tenantID), entries[3].Message) | ||
require.Len(t, entries, 17) | ||
assert.Equal(t, "tenant unhcr added successfully", entries[15].Message) | ||
assert.Contains(t, fmt.Sprintf("tenant ID: %s", tenantID), entries[16].Message) | ||
|
||
// Connecting to the new schema | ||
schemaName := fmt.Sprintf("sdp_%s", tenantName) | ||
dataSourceName := dbConnectionPool.DSN() | ||
u, err := url.Parse(dataSourceName) | ||
require.NoError(t, err) | ||
uq := u.Query() | ||
uq.Set("search_path", schemaName) | ||
u.RawQuery = uq.Encode() | ||
|
||
tenantSchemaConnectionPool, err := db.OpenDBConnectionPool(u.String()) | ||
require.NoError(t, err) | ||
defer tenantSchemaConnectionPool.Close() | ||
|
||
expectedTablesAfterMigrationsApplied := []string{ | ||
"assets", | ||
"auth_migrations", | ||
"auth_user_mfa_codes", | ||
"auth_user_password_reset", | ||
"auth_users", | ||
"channel_accounts", | ||
"countries", | ||
"disbursements", | ||
"gorp_migrations", | ||
"messages", | ||
"organizations", | ||
"payments", | ||
"receiver_verifications", | ||
"receiver_wallets", | ||
"receivers", | ||
"submitter_transactions", | ||
"wallets", | ||
"wallets_assets", | ||
} | ||
tenant.TenantSchemaHasTablesFixture(t, ctx, tenantSchemaConnectionPool, schemaName, expectedTablesAfterMigrationsApplied) | ||
tenant.AssertRegisteredAssets(t, ctx, tenantSchemaConnectionPool, []string{"USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", "XLM:"}) | ||
tenant.AssertRegisteredWallets(t, ctx, tenantSchemaConnectionPool, []string{"Demo Wallet", "Vibrant Assist"}) | ||
tenant.AssertRegisteredUser(t, ctx, tenantSchemaConnectionPool, userFirstName, userLastName, userEmail) | ||
}) | ||
|
||
t.Run("adds new tenant successfully pubnet", func(t *testing.T) { | ||
tenantName := "irc" | ||
userFirstName := "First" | ||
userLastName := "Last" | ||
userEmail := "[email protected]" | ||
|
||
out := new(strings.Builder) | ||
rootCmd := rootCmd() | ||
rootCmd.AddCommand(AddTenantsCmd()) | ||
rootCmd.SetOut(out) | ||
rootCmd.SetErr(out) | ||
rootCmd.SetArgs([]string{"add-tenants", tenantName, userFirstName, userLastName, userEmail, "--network-type", "pubnet", "--multitenant-db-url", dbt.DSN}) | ||
getEntries := log.DefaultLogger.StartTest(log.InfoLevel) | ||
|
||
err := rootCmd.ExecuteContext(ctx) | ||
require.NoError(t, err) | ||
assert.Empty(t, out.String()) | ||
|
||
const q = "SELECT id FROM tenants WHERE name = $1" | ||
var tenantID string | ||
err = dbConnectionPool.GetContext(ctx, &tenantID, q, tenantName) | ||
require.NoError(t, err) | ||
|
||
entries := getEntries() | ||
require.Len(t, entries, 17) | ||
assert.Equal(t, "tenant irc added successfully", entries[15].Message) | ||
assert.Contains(t, fmt.Sprintf("tenant ID: %s", tenantID), entries[16].Message) | ||
|
||
// Connecting to the new schema | ||
schemaName := fmt.Sprintf("sdp_%s", tenantName) | ||
dataSourceName := dbConnectionPool.DSN() | ||
u, err := url.Parse(dataSourceName) | ||
require.NoError(t, err) | ||
uq := u.Query() | ||
uq.Set("search_path", schemaName) | ||
u.RawQuery = uq.Encode() | ||
|
||
tenantSchemaConnectionPool, err := db.OpenDBConnectionPool(u.String()) | ||
require.NoError(t, err) | ||
defer tenantSchemaConnectionPool.Close() | ||
|
||
expectedTablesAfterMigrationsApplied := []string{ | ||
"assets", | ||
"auth_migrations", | ||
"auth_user_mfa_codes", | ||
"auth_user_password_reset", | ||
"auth_users", | ||
"channel_accounts", | ||
"countries", | ||
"disbursements", | ||
"gorp_migrations", | ||
"messages", | ||
"organizations", | ||
"payments", | ||
"receiver_verifications", | ||
"receiver_wallets", | ||
"receivers", | ||
"submitter_transactions", | ||
"wallets", | ||
"wallets_assets", | ||
} | ||
tenant.TenantSchemaHasTablesFixture(t, ctx, tenantSchemaConnectionPool, schemaName, expectedTablesAfterMigrationsApplied) | ||
tenant.AssertRegisteredAssets(t, ctx, tenantSchemaConnectionPool, []string{"USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN", "XLM:"}) | ||
tenant.AssertRegisteredWallets(t, ctx, tenantSchemaConnectionPool, []string{"Vibrant Assist RC", "Vibrant Assist"}) | ||
tenant.AssertRegisteredUser(t, ctx, tenantSchemaConnectionPool, userFirstName, userLastName, userEmail) | ||
}) | ||
} |
Oops, something went wrong.