Previously we were using permission ids to check if two permissions are equal. While we were aware the permission ids can repeat across different folder hierarchies we were not aware that this was also the case within a single folder tree if we delete and break the inheritance chain. Assume the following scenario: 1. Create folder `a` and `a/b` 2. Assign user1:read permission to `a` 3. Remove the inherited permission from `a/b` 4. Assign user1:write permission to `a/b` In this case both the user1:read and user1:write will get the same permissions id.<!-- PR description--> --- #### Does this PR need a docs update or release note? - [ ] ✅ Yes, it's included - [ ] 🕐 Yes, but in a later PR - [x] ⛔ No #### Type of change <!--- Please check the type of change your PR introduces: ---> - [ ] 🌻 Feature - [x] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Supportability/Tests - [ ] 💻 CI/Deployment - [ ] 🧹 Tech Debt/Cleanup #### Issue(s) <!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. --> * fixes https://github.com/alcionai/corso/issues/3116 #### Test Plan <!-- How will this be tested prior to merging.--> - [ ] 💪 Manual - [x] ⚡ Unit test - [ ] 💚 E2E
220 lines
6.5 KiB
Go
220 lines
6.5 KiB
Go
package tester
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/alcionai/clues"
|
|
"github.com/spf13/viper"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/exp/maps"
|
|
|
|
"github.com/alcionai/corso/src/pkg/account"
|
|
)
|
|
|
|
const (
|
|
// S3 config
|
|
TestCfgBucket = "bucket"
|
|
TestCfgEndpoint = "endpoint"
|
|
TestCfgPrefix = "prefix"
|
|
TestCfgStorageProvider = "provider"
|
|
|
|
// M365 config
|
|
TestCfgAzureTenantID = "azure_tenantid"
|
|
TestCfgSiteID = "m365siteid"
|
|
TestCfgSiteURL = "m365siteurl"
|
|
TestCfgUserID = "m365userid"
|
|
TestCfgSecondaryUserID = "secondarym365userid"
|
|
TestCfgTertiaryUserID = "tertiarym365userid"
|
|
TestCfgLoadTestUserID = "loadtestm365userid"
|
|
TestCfgLoadTestOrgUsers = "loadtestm365orgusers"
|
|
TestCfgAccountProvider = "account_provider"
|
|
)
|
|
|
|
// test specific env vars
|
|
const (
|
|
EnvCorsoM365TestSiteID = "CORSO_M365_TEST_SITE_ID"
|
|
EnvCorsoM365TestSiteURL = "CORSO_M365_TEST_SITE_URL"
|
|
EnvCorsoM365TestUserID = "CORSO_M365_TEST_USER_ID"
|
|
EnvCorsoSecondaryM365TestUserID = "CORSO_SECONDARY_M365_TEST_USER_ID"
|
|
EnvCorsoTertiaryM365TestUserID = "CORSO_TERTIARY_M365_TEST_USER_ID"
|
|
EnvCorsoM365LoadTestUserID = "CORSO_M365_LOAD_TEST_USER_ID"
|
|
EnvCorsoM365LoadTestOrgUsers = "CORSO_M365_LOAD_TEST_ORG_USERS"
|
|
EnvCorsoTestConfigFilePath = "CORSO_TEST_CONFIG_FILE"
|
|
)
|
|
|
|
// global to hold the test config results.
|
|
var testConfig map[string]string
|
|
|
|
// call this instead of returning testConfig directly.
|
|
func cloneTestConfig() map[string]string {
|
|
if testConfig == nil {
|
|
return map[string]string{}
|
|
}
|
|
|
|
return maps.Clone(testConfig)
|
|
}
|
|
|
|
func NewTestViper() (*viper.Viper, error) {
|
|
vpr := viper.New()
|
|
|
|
configFilePath := os.Getenv(EnvCorsoTestConfigFilePath)
|
|
if len(configFilePath) == 0 {
|
|
return vpr, nil
|
|
}
|
|
|
|
// Or use a custom file location
|
|
ext := filepath.Ext(configFilePath)
|
|
if len(ext) == 0 {
|
|
return nil, clues.New("corso_test requires an extension")
|
|
}
|
|
|
|
vpr.SetConfigFile(configFilePath)
|
|
vpr.AddConfigPath(filepath.Dir(configFilePath))
|
|
vpr.SetConfigType(ext[1:])
|
|
|
|
fileName := filepath.Base(configFilePath)
|
|
fileName = strings.TrimSuffix(fileName, ext)
|
|
vpr.SetConfigName(fileName)
|
|
|
|
return vpr, nil
|
|
}
|
|
|
|
// reads a corso configuration file with values specific to
|
|
// local integration test controls. Populates values with
|
|
// defaults where standard.
|
|
func readTestConfig() (map[string]string, error) {
|
|
if testConfig != nil {
|
|
return cloneTestConfig(), nil
|
|
}
|
|
|
|
vpr, err := NewTestViper()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// only error if reading an existing file failed. No problem if we're missing files.
|
|
if err = vpr.ReadInConfig(); err != nil {
|
|
_, ok := err.(viper.ConfigFileNotFoundError)
|
|
if !ok {
|
|
return nil, clues.Wrap(err, "reading config file: "+viper.ConfigFileUsed())
|
|
}
|
|
}
|
|
|
|
testEnv := map[string]string{}
|
|
fallbackTo(testEnv, TestCfgStorageProvider, vpr.GetString(TestCfgStorageProvider))
|
|
fallbackTo(testEnv, TestCfgAccountProvider, vpr.GetString(TestCfgAccountProvider))
|
|
fallbackTo(testEnv, TestCfgBucket, vpr.GetString(TestCfgBucket), "test-corso-repo-init")
|
|
fallbackTo(testEnv, TestCfgEndpoint, vpr.GetString(TestCfgEndpoint), "s3.amazonaws.com")
|
|
fallbackTo(testEnv, TestCfgPrefix, vpr.GetString(TestCfgPrefix))
|
|
fallbackTo(testEnv, TestCfgAzureTenantID, os.Getenv(account.AzureTenantID), vpr.GetString(TestCfgAzureTenantID))
|
|
fallbackTo(
|
|
testEnv,
|
|
TestCfgUserID,
|
|
os.Getenv(EnvCorsoM365TestUserID),
|
|
vpr.GetString(TestCfgUserID),
|
|
"LynneR@10rqc2.onmicrosoft.com")
|
|
fallbackTo(
|
|
testEnv,
|
|
TestCfgSecondaryUserID,
|
|
os.Getenv(EnvCorsoSecondaryM365TestUserID),
|
|
vpr.GetString(TestCfgSecondaryUserID),
|
|
"AdeleV@10rqc2.onmicrosoft.com")
|
|
fallbackTo(
|
|
testEnv,
|
|
TestCfgTertiaryUserID,
|
|
os.Getenv(EnvCorsoTertiaryM365TestUserID),
|
|
vpr.GetString(TestCfgTertiaryUserID),
|
|
"PradeepG@10rqc2.onmicrosoft.com")
|
|
fallbackTo(
|
|
testEnv,
|
|
TestCfgLoadTestUserID,
|
|
os.Getenv(EnvCorsoM365LoadTestUserID),
|
|
vpr.GetString(TestCfgLoadTestUserID),
|
|
"leeg@10rqc2.onmicrosoft.com")
|
|
fallbackTo(
|
|
testEnv,
|
|
TestCfgLoadTestOrgUsers,
|
|
os.Getenv(EnvCorsoM365LoadTestOrgUsers),
|
|
vpr.GetString(TestCfgLoadTestOrgUsers),
|
|
"AdeleV@10rqc2.onmicrosoft.com,LynneR@10rqc2.onmicrosoft.com")
|
|
fallbackTo(
|
|
testEnv,
|
|
TestCfgSiteID,
|
|
os.Getenv(EnvCorsoM365TestSiteID),
|
|
vpr.GetString(TestCfgSiteID),
|
|
"10rqc2.sharepoint.com,4892edf5-2ebf-46be-a6e5-a40b2cbf1c1a,38ab6d06-fc82-4417-af93-22d8733c22be")
|
|
fallbackTo(
|
|
testEnv,
|
|
TestCfgSiteURL,
|
|
os.Getenv(EnvCorsoM365TestSiteURL),
|
|
vpr.GetString(TestCfgSiteURL),
|
|
"https://10rqc2.sharepoint.com/sites/CorsoCI")
|
|
|
|
testEnv[EnvCorsoTestConfigFilePath] = os.Getenv(EnvCorsoTestConfigFilePath)
|
|
testConfig = testEnv
|
|
|
|
return cloneTestConfig(), nil
|
|
}
|
|
|
|
// MakeTempTestConfigClone makes a copy of the test config file in a temp directory.
|
|
// This allows tests which interface with reading and writing to a config file
|
|
// (such as the CLI) to safely manipulate file contents without amending the user's
|
|
// original file.
|
|
//
|
|
// Attempts to copy values sourced from the caller's test config file.
|
|
// The overrides prop replaces config values with the provided value.
|
|
//
|
|
// Returns a filepath string pointing to the location of the temp file.
|
|
func MakeTempTestConfigClone(t *testing.T, overrides map[string]string) (*viper.Viper, string) {
|
|
cfg, err := readTestConfig()
|
|
require.NoError(t, err, "reading tester config", clues.ToCore(err))
|
|
|
|
fName := filepath.Base(os.Getenv(EnvCorsoTestConfigFilePath))
|
|
if len(fName) == 0 || fName == "." || fName == "/" {
|
|
fName = ".corso_test.toml"
|
|
}
|
|
|
|
tDir := t.TempDir()
|
|
tDirFp := filepath.Join(tDir, fName)
|
|
|
|
_, err = os.Create(tDirFp)
|
|
require.NoError(t, err, "creating temp test dir", clues.ToCore(err))
|
|
|
|
ext := filepath.Ext(fName)
|
|
vpr := viper.New()
|
|
vpr.SetConfigFile(tDirFp)
|
|
vpr.AddConfigPath(tDir)
|
|
vpr.SetConfigType(strings.TrimPrefix(ext, "."))
|
|
vpr.SetConfigName(strings.TrimSuffix(fName, ext))
|
|
vpr.Set("corso-testing", true)
|
|
|
|
for k, v := range cfg {
|
|
vpr.Set(k, v)
|
|
}
|
|
|
|
for k, v := range overrides {
|
|
vpr.Set(k, v)
|
|
}
|
|
|
|
err = vpr.WriteConfig()
|
|
require.NoError(t, err, "writing temp dir viper config file", clues.ToCore(err))
|
|
|
|
return vpr, tDirFp
|
|
}
|
|
|
|
// writes the first non-zero valued string to the map at the key.
|
|
// fallback priority should match viper ordering (manually handled
|
|
// here since viper fails to provide fallbacks on fileNotFoundErr):
|
|
// manual overrides > flags > env vars > config file > default value
|
|
func fallbackTo(m map[string]string, key string, fallbacks ...string) {
|
|
for _, fb := range fallbacks {
|
|
if len(fb) > 0 {
|
|
m[key] = fb
|
|
return
|
|
}
|
|
}
|
|
}
|