Replace the entity slugs on organizations, workspaces, resource pools, and plan ladders with nullable `key` columns and add keys to products, prices, and entitlement sets. Rename `providers.slug` to `provider` and add partial unique indexes for system and org role names. Assign invoice numbers per billing account from a gapless transactional counter; Stripe's number moves to the invoice mapping as an external reference. Seeds, fixtures, and the operator lookup address rows by key, and the returning-login resync no longer blanks a display name when the IdP sends no `name` claim.
148 lines
4.2 KiB
Go
148 lines
4.2 KiB
Go
package integration
|
|
|
|
import "testing"
|
|
|
|
func TestValidateManifest(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
m Manifest
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "valid provisioning provider",
|
|
m: Manifest{
|
|
Key: "example",
|
|
Kind: KindProvisioning,
|
|
DisplayName: "Example",
|
|
Operations: []Operation{OpCreate, OpDelete, OpList},
|
|
ResourceKeys: []string{"example_sites"},
|
|
},
|
|
},
|
|
{
|
|
name: "valid payment provider with no ops or keys",
|
|
m: Manifest{Key: "stripe", Kind: KindPayment, DisplayName: "Stripe"},
|
|
},
|
|
{
|
|
name: "key with underscore is rejected",
|
|
m: Manifest{Key: "next_cloud", Kind: KindProvisioning, DisplayName: "NextCloud"},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "key with uppercase is rejected",
|
|
m: Manifest{Key: "Example", Kind: KindProvisioning, DisplayName: "Example"},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "unknown kind is rejected",
|
|
m: Manifest{Key: "example", Kind: ProviderKind("storage"), DisplayName: "Example"},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "unknown operation is rejected",
|
|
m: Manifest{Key: "example", Kind: KindProvisioning, DisplayName: "Example", Operations: []Operation{"archive"}},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "resource key not prefixed with the provider key is rejected",
|
|
m: Manifest{Key: "example", Kind: KindProvisioning, DisplayName: "Example", ResourceKeys: []string{"sites"}},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "resource key prefixed by a different provider is rejected",
|
|
m: Manifest{Key: "example", Kind: KindProvisioning, DisplayName: "Example", ResourceKeys: []string{"nextcloud_files"}},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "set_status with active plus a non-active state is valid",
|
|
m: Manifest{
|
|
Key: "example", Kind: KindProvisioning, DisplayName: "Example",
|
|
Operations: []Operation{OpCreate, OpSetStatus, OpDelete, OpList},
|
|
States: []State{StateActive, StateReadonly, StateArchived},
|
|
},
|
|
},
|
|
{
|
|
name: "set_status without a non-active state is rejected",
|
|
m: Manifest{
|
|
Key: "example", Kind: KindProvisioning, DisplayName: "Example",
|
|
Operations: []Operation{OpSetStatus, OpList},
|
|
States: []State{StateActive},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "set_status without the active state is rejected",
|
|
m: Manifest{
|
|
Key: "example", Kind: KindProvisioning, DisplayName: "Example",
|
|
Operations: []Operation{OpSetStatus, OpList},
|
|
States: []State{StateReadonly},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "states declared without set_status are rejected",
|
|
m: Manifest{
|
|
Key: "example", Kind: KindProvisioning, DisplayName: "Example",
|
|
Operations: []Operation{OpCreate, OpList},
|
|
States: []State{StateActive, StateReadonly},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "malformed state value is rejected",
|
|
m: Manifest{
|
|
Key: "example", Kind: KindProvisioning, DisplayName: "Example",
|
|
Operations: []Operation{OpSetStatus, OpList},
|
|
States: []State{StateActive, "Read-Only"},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validateManifest(tt.m)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Fatalf("validateManifest() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCheckKeyNesting(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
providerKeys []string
|
|
platformKeys []string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "no collision",
|
|
providerKeys: []string{"example", "nextcloud"},
|
|
platformKeys: []string{"storage_bytes", "seats"},
|
|
},
|
|
{
|
|
name: "provider key equals a platform key leading token",
|
|
providerKeys: []string{"storage"},
|
|
platformKeys: []string{"storage_bytes"},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "provider key equals a single-token platform key",
|
|
providerKeys: []string{"seats"},
|
|
platformKeys: []string{"seats"},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
set := make(map[string]bool, len(tt.providerKeys))
|
|
for _, s := range tt.providerKeys {
|
|
set[s] = true
|
|
}
|
|
err := checkKeyNesting(set, tt.platformKeys)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Fatalf("checkKeyNesting() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
})
|
|
}
|
|
}
|