sanity test refactor (#4370)
refactoring the sanity tests with three goals: 1. move from env vars to cli commands so that unsupported commands fail loudly. 2. set up support for groups restore and export testing. 3. introduce some code re-use throughout. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🤖 Supportability/Tests #### Issue(s) * #3988 #### Test Plan - [x] 💚 E2E
This commit is contained in:
parent
a5f93f7a10
commit
9e0d464854
53
.github/actions/backup-restore-test/action.yml
vendored
53
.github/actions/backup-restore-test/action.yml
vendored
@ -45,6 +45,9 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
working-directory: src
|
working-directory: src
|
||||||
run: |
|
run: |
|
||||||
|
echo "---------------------------"
|
||||||
|
echo Backup ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
|
echo "---------------------------"
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-backup-${{ inputs.service }}-${{inputs.kind }}.log
|
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-backup-${{ inputs.service }}-${{inputs.kind }}.log
|
||||||
./corso backup create '${{ inputs.service }}' \
|
./corso backup create '${{ inputs.service }}' \
|
||||||
@ -61,6 +64,9 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
working-directory: src
|
working-directory: src
|
||||||
run: |
|
run: |
|
||||||
|
echo "---------------------------"
|
||||||
|
echo Restore ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
|
echo "---------------------------"
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-restore-${{ inputs.service }}-${{inputs.kind }}.log
|
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-restore-${{ inputs.service }}-${{inputs.kind }}.log
|
||||||
./corso restore '${{ inputs.service }}' \
|
./corso restore '${{ inputs.service }}' \
|
||||||
@ -85,11 +91,14 @@ runs:
|
|||||||
SANITY_TEST_KIND: restore
|
SANITY_TEST_KIND: restore
|
||||||
SANITY_TEST_FOLDER: ${{ steps.restore.outputs.result }}
|
SANITY_TEST_FOLDER: ${{ steps.restore.outputs.result }}
|
||||||
SANITY_TEST_SERVICE: ${{ inputs.service }}
|
SANITY_TEST_SERVICE: ${{ inputs.service }}
|
||||||
TEST_DATA: ${{ inputs.test-folder }}
|
SANITY_TEST_DATA: ${{ inputs.test-folder }}
|
||||||
BASE_BACKUP: ${{ inputs.base-backup }}
|
SANITY_BASE_BACKUP: ${{ inputs.base-backup }}
|
||||||
run: |
|
run: |
|
||||||
|
echo "---------------------------"
|
||||||
|
echo Sanity Test Restore ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
|
echo "---------------------------"
|
||||||
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-validate-${{ inputs.service }}-${{inputs.kind }}.log
|
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-validate-${{ inputs.service }}-${{inputs.kind }}.log
|
||||||
./sanity-test
|
./sanity-test restore ${{ inputs.service }}
|
||||||
|
|
||||||
- name: Export ${{ inputs.service }} ${{ inputs.kind }}
|
- name: Export ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
if: inputs.with-export == true
|
if: inputs.with-export == true
|
||||||
@ -97,6 +106,9 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
working-directory: src
|
working-directory: src
|
||||||
run: |
|
run: |
|
||||||
|
echo "---------------------------"
|
||||||
|
echo Export ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
|
echo "---------------------------"
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-restore-${{ inputs.service }}-${{inputs.kind }}.log
|
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-restore-${{ inputs.service }}-${{inputs.kind }}.log
|
||||||
./corso export '${{ inputs.service }}' \
|
./corso export '${{ inputs.service }}' \
|
||||||
@ -116,11 +128,14 @@ runs:
|
|||||||
SANITY_TEST_KIND: export
|
SANITY_TEST_KIND: export
|
||||||
SANITY_TEST_FOLDER: /tmp/export-${{ inputs.service }}-${{inputs.kind }}
|
SANITY_TEST_FOLDER: /tmp/export-${{ inputs.service }}-${{inputs.kind }}
|
||||||
SANITY_TEST_SERVICE: ${{ inputs.service }}
|
SANITY_TEST_SERVICE: ${{ inputs.service }}
|
||||||
TEST_DATA: ${{ inputs.test-folder }}
|
SANITY_TEST_DATA: ${{ inputs.test-folder }}
|
||||||
BASE_BACKUP: ${{ inputs.base-backup }}
|
SANITY_BASE_BACKUP: ${{ inputs.base-backup }}
|
||||||
run: |
|
run: |
|
||||||
|
echo "---------------------------"
|
||||||
|
echo Sanity-Test Export ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
|
echo "---------------------------"
|
||||||
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-validate-${{ inputs.service }}-${{inputs.kind }}.log
|
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-validate-${{ inputs.service }}-${{inputs.kind }}.log
|
||||||
./sanity-test
|
./sanity-test export ${{ inputs.service }}
|
||||||
|
|
||||||
- name: Export archive ${{ inputs.service }} ${{ inputs.kind }}
|
- name: Export archive ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
if: inputs.with-export == true
|
if: inputs.with-export == true
|
||||||
@ -128,6 +143,9 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
working-directory: src
|
working-directory: src
|
||||||
run: |
|
run: |
|
||||||
|
echo "---------------------------"
|
||||||
|
echo Export Archive ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
|
echo "---------------------------"
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-restore-${{ inputs.service }}-${{inputs.kind }}.log
|
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-restore-${{ inputs.service }}-${{inputs.kind }}.log
|
||||||
./corso export '${{ inputs.service }}' \
|
./corso export '${{ inputs.service }}' \
|
||||||
@ -150,16 +168,22 @@ runs:
|
|||||||
SANITY_TEST_KIND: export
|
SANITY_TEST_KIND: export
|
||||||
SANITY_TEST_FOLDER: /tmp/export-${{ inputs.service }}-${{inputs.kind }}-unzipped
|
SANITY_TEST_FOLDER: /tmp/export-${{ inputs.service }}-${{inputs.kind }}-unzipped
|
||||||
SANITY_TEST_SERVICE: ${{ inputs.service }}
|
SANITY_TEST_SERVICE: ${{ inputs.service }}
|
||||||
TEST_DATA: ${{ inputs.test-folder }}
|
SANITY_TEST_DATA: ${{ inputs.test-folder }}
|
||||||
BASE_BACKUP: ${{ inputs.base-backup }}
|
SANITY_BASE_BACKUP: ${{ inputs.base-backup }}
|
||||||
run: |
|
run: |
|
||||||
|
echo "---------------------------"
|
||||||
|
echo Sanity-Test Export Archive ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
|
echo "---------------------------"
|
||||||
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-validate-${{ inputs.service }}-${{inputs.kind }}.log
|
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-validate-${{ inputs.service }}-${{inputs.kind }}.log
|
||||||
./sanity-test
|
./sanity-test export ${{ inputs.service }}
|
||||||
|
|
||||||
- name: List ${{ inputs.service }} ${{ inputs.kind }}
|
- name: List ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: src
|
working-directory: src
|
||||||
run: |
|
run: |
|
||||||
|
echo "---------------------------"
|
||||||
|
echo Backup list ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
|
echo "---------------------------"
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-backup-list-${{ inputs.service }}-${{inputs.kind }}.log
|
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-backup-list-${{ inputs.service }}-${{inputs.kind }}.log
|
||||||
./corso backup list ${{ inputs.service }} \
|
./corso backup list ${{ inputs.service }} \
|
||||||
@ -178,6 +202,9 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
working-directory: src
|
working-directory: src
|
||||||
run: |
|
run: |
|
||||||
|
echo "---------------------------"
|
||||||
|
echo Backup List w/ Backup ${{ inputs.service }} ${{ inputs.kind }}
|
||||||
|
echo "---------------------------"
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-backup-list-single-${{ inputs.service }}-${{inputs.kind }}.log
|
CORSO_LOG_FILE=${{ inputs.log-dir }}/gotest-backup-list-single-${{ inputs.service }}-${{inputs.kind }}.log
|
||||||
./corso backup list ${{ inputs.service }} \
|
./corso backup list ${{ inputs.service }} \
|
||||||
@ -193,7 +220,13 @@ runs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Upload the original go test output as an artifact for later review.
|
- if: always()
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "---------------------------"
|
||||||
|
echo Logging Results
|
||||||
|
echo "---------------------------"
|
||||||
|
|
||||||
- name: Upload test log
|
- name: Upload test log
|
||||||
if: always()
|
if: always()
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
|||||||
4
.github/actions/slack-message/action.yml
vendored
4
.github/actions/slack-message/action.yml
vendored
@ -31,7 +31,7 @@ runs:
|
|||||||
- name: use url or blank val
|
- name: use url or blank val
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
echo "STEP=${{ github.action || '' }}" >> $GITHUB_ENV
|
echo "STEP=${{ env.trimmed_ref || '' }}" >> $GITHUB_ENV
|
||||||
echo "JOB=${{ github.job || '' }}" >> $GITHUB_ENV
|
echo "JOB=${{ github.job || '' }}" >> $GITHUB_ENV
|
||||||
echo "LOGS=${{ github.run_id && env.logurl || '-' }}" >> $GITHUB_ENV
|
echo "LOGS=${{ github.run_id && env.logurl || '-' }}" >> $GITHUB_ENV
|
||||||
echo "COMMIT=${{ github.sha && env.commiturl || '-' }}" >> $GITHUB_ENV
|
echo "COMMIT=${{ github.sha && env.commiturl || '-' }}" >> $GITHUB_ENV
|
||||||
@ -51,7 +51,7 @@ runs:
|
|||||||
"type": "section",
|
"type": "section",
|
||||||
"text": {
|
"text": {
|
||||||
"type": "mrkdwn",
|
"type": "mrkdwn",
|
||||||
"text": "${{ inputs.msg }} :: ${{ env.JOB }} - ${{ env.STEP }}\n${{ env.LOGS }} ${{ env.COMMIT }} ${{ env.REF }}"
|
"text": "${{ inputs.msg }}\n${{ env.JOB }} :: ${{ env.STEP }}\n${{ env.LOGS }} ${{ env.COMMIT }} ${{ env.REF }}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
32
.github/workflows/sanity-test.yaml
vendored
32
.github/workflows/sanity-test.yaml
vendored
@ -181,7 +181,7 @@ jobs:
|
|||||||
uses: ./.github/actions/backup-restore-test
|
uses: ./.github/actions/backup-restore-test
|
||||||
with:
|
with:
|
||||||
service: exchange
|
service: exchange
|
||||||
kind: initial
|
kind: first-backup
|
||||||
backup-args: '--mailbox "${{ env.TEST_USER }}" --data "email"'
|
backup-args: '--mailbox "${{ env.TEST_USER }}" --data "email"'
|
||||||
restore-args: '--email-folder ${{ env.RESTORE_DEST_PFX }}${{ steps.repo-init.outputs.result }}'
|
restore-args: '--email-folder ${{ env.RESTORE_DEST_PFX }}${{ steps.repo-init.outputs.result }}'
|
||||||
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.repo-init.outputs.result }}'
|
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.repo-init.outputs.result }}'
|
||||||
@ -249,7 +249,7 @@ jobs:
|
|||||||
uses: ./.github/actions/backup-restore-test
|
uses: ./.github/actions/backup-restore-test
|
||||||
with:
|
with:
|
||||||
service: onedrive
|
service: onedrive
|
||||||
kind: initial
|
kind: first-backup
|
||||||
backup-args: '--user "${{ env.TEST_USER }}"'
|
backup-args: '--user "${{ env.TEST_USER }}"'
|
||||||
restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }}'
|
restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }}'
|
||||||
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }}'
|
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-onedrive.outputs.result }}'
|
||||||
@ -305,7 +305,7 @@ jobs:
|
|||||||
uses: ./.github/actions/backup-restore-test
|
uses: ./.github/actions/backup-restore-test
|
||||||
with:
|
with:
|
||||||
service: sharepoint
|
service: sharepoint
|
||||||
kind: initial
|
kind: first-backup
|
||||||
backup-args: '--site "${{ secrets.CORSO_M365_TEST_SITE_URL }}"'
|
backup-args: '--site "${{ secrets.CORSO_M365_TEST_SITE_URL }}"'
|
||||||
restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }}'
|
restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }}'
|
||||||
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }}'
|
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-sharepoint.outputs.result }}'
|
||||||
@ -362,12 +362,34 @@ jobs:
|
|||||||
uses: ./.github/actions/backup-restore-test
|
uses: ./.github/actions/backup-restore-test
|
||||||
with:
|
with:
|
||||||
service: groups
|
service: groups
|
||||||
kind: initial
|
kind: first-backup
|
||||||
backup-args: '--group "${{ vars.CORSO_M365_TEST_TEAM_ID }}"'
|
backup-args: '--group "${{ vars.CORSO_M365_TEST_TEAM_ID }}"'
|
||||||
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-groups.outputs.result }}'
|
test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-groups.outputs.result }}'
|
||||||
log-dir: ${{ env.CORSO_LOG_DIR }}
|
log-dir: ${{ env.CORSO_LOG_DIR }}
|
||||||
|
|
||||||
# TODO: incrementals
|
# generate some more enteries for incremental check
|
||||||
|
# - name: Groups - Create new data (for incremental)
|
||||||
|
# working-directory: ./src/cmd/factory
|
||||||
|
# run: |
|
||||||
|
# go run . sharepoint files \
|
||||||
|
# --site ${{ secrets.CORSO_M365_TEST_GROUPS_SITE_URL }} \
|
||||||
|
# --user ${{ env.TEST_USER }} \
|
||||||
|
# --secondaryuser ${{ env.CORSO_SECONDARY_M365_TEST_USER_ID }} \
|
||||||
|
# --tenant ${{ secrets.TENANT_ID }} \
|
||||||
|
# --destination ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-groups.outputs.result }} \
|
||||||
|
# --count 4
|
||||||
|
|
||||||
|
# - name: Groups - Incremental backup
|
||||||
|
# id: groups-incremental
|
||||||
|
# uses: ./.github/actions/backup-restore-test
|
||||||
|
# with:
|
||||||
|
# service: groups
|
||||||
|
# kind: incremental
|
||||||
|
# backup-args: '--site "${{ secrets.CORSO_M365_TEST_GROUPS_SITE_URL }}"'
|
||||||
|
# restore-args: '--folder ${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-groups.outputs.result }}'
|
||||||
|
# test-folder: '${{ env.RESTORE_DEST_PFX }}${{ steps.new-data-creation-groups.outputs.result }}'
|
||||||
|
# log-dir: ${{ env.CORSO_LOG_DIR }}
|
||||||
|
# with-export: true
|
||||||
|
|
||||||
##########################################################################################################################################
|
##########################################################################################################################################
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,68 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/internal/tester/tconfig"
|
||||||
|
"github.com/alcionai/corso/src/pkg/account"
|
||||||
|
"github.com/alcionai/corso/src/pkg/control"
|
||||||
|
"github.com/alcionai/corso/src/pkg/credentials"
|
||||||
|
"github.com/alcionai/corso/src/pkg/logger"
|
||||||
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||||
|
)
|
||||||
|
|
||||||
type PermissionInfo struct {
|
type PermissionInfo struct {
|
||||||
EntityID string
|
EntityID string
|
||||||
Roles []string
|
Roles []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
sanityBaseBackup = "SANITY_BASE_BACKUP"
|
||||||
|
sanityTestData = "SANITY_TEST_DATA"
|
||||||
|
sanityTestFolder = "SANITY_TEST_FOLDER"
|
||||||
|
sanityTestService = "SANITY_TEST_SERVICE"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Envs struct {
|
||||||
|
BaseBackupFolder string
|
||||||
|
DataFolder string
|
||||||
|
FolderName string
|
||||||
|
Service string
|
||||||
|
SiteID string
|
||||||
|
StartTime time.Time
|
||||||
|
UserID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func EnvVars(ctx context.Context) Envs {
|
||||||
|
folder := strings.TrimSpace(os.Getenv(sanityTestFolder))
|
||||||
|
startTime, _ := MustGetTimeFromName(ctx, folder)
|
||||||
|
|
||||||
|
e := Envs{
|
||||||
|
BaseBackupFolder: os.Getenv(sanityBaseBackup),
|
||||||
|
DataFolder: os.Getenv(sanityTestData),
|
||||||
|
FolderName: folder,
|
||||||
|
SiteID: tconfig.GetM365SiteID(ctx),
|
||||||
|
Service: os.Getenv(sanityTestService),
|
||||||
|
StartTime: startTime,
|
||||||
|
UserID: tconfig.GetM365UserID(ctx),
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n-----\nenvs %+v\n-----\n", e)
|
||||||
|
|
||||||
|
logger.Ctx(ctx).Info("envs", e)
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetAC() (api.Client, error) {
|
||||||
|
creds := account.M365Config{
|
||||||
|
M365: credentials.GetM365(),
|
||||||
|
AzureTenantID: os.Getenv(account.AzureTenantID),
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.NewClient(creds, control.DefaultOptions())
|
||||||
|
}
|
||||||
|
|||||||
38
src/cmd/sanity_test/common/filepath.go
Normal file
38
src/cmd/sanity_test/common/filepath.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alcionai/clues"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FilepathWalker(
|
||||||
|
folderName string,
|
||||||
|
exportFileSizes map[string]int64,
|
||||||
|
startTime time.Time,
|
||||||
|
) filepath.WalkFunc {
|
||||||
|
return func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return clues.Stack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
relPath, err := filepath.Rel(folderName, path)
|
||||||
|
if err != nil {
|
||||||
|
return clues.Stack(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
exportFileSizes[relPath] = info.Size()
|
||||||
|
|
||||||
|
if startTime.After(info.ModTime()) {
|
||||||
|
startTime = info.ModTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/cmd/sanity_test/common/sanitree.go
Normal file
69
src/cmd/sanity_test/common/sanitree.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sanitree is used to build out a hierarchical tree of items
|
||||||
|
// for comparison against each other. Primarily so that a restore
|
||||||
|
// can compare two subtrees easily.
|
||||||
|
type Sanitree[T any] struct {
|
||||||
|
Container T
|
||||||
|
ContainerID string
|
||||||
|
ContainerName string
|
||||||
|
// non-containers only
|
||||||
|
ContainsItems int
|
||||||
|
// name -> node
|
||||||
|
Children map[string]*Sanitree[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertEqualTrees[T any](
|
||||||
|
ctx context.Context,
|
||||||
|
expect, other *Sanitree[T],
|
||||||
|
) {
|
||||||
|
if expect == nil && other == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(
|
||||||
|
ctx,
|
||||||
|
func() bool { return expect != nil && other != nil },
|
||||||
|
"non nil nodes",
|
||||||
|
expect,
|
||||||
|
other)
|
||||||
|
|
||||||
|
Assert(
|
||||||
|
ctx,
|
||||||
|
func() bool { return expect.ContainerName == other.ContainerName },
|
||||||
|
"container names match",
|
||||||
|
expect.ContainerName,
|
||||||
|
other.ContainerName)
|
||||||
|
|
||||||
|
Assert(
|
||||||
|
ctx,
|
||||||
|
func() bool { return expect.ContainsItems == other.ContainsItems },
|
||||||
|
"count of items in container matches",
|
||||||
|
expect.ContainsItems,
|
||||||
|
other.ContainsItems)
|
||||||
|
|
||||||
|
Assert(
|
||||||
|
ctx,
|
||||||
|
func() bool { return len(expect.Children) == len(other.Children) },
|
||||||
|
"count of child containers matches",
|
||||||
|
len(expect.Children),
|
||||||
|
len(other.Children))
|
||||||
|
|
||||||
|
for name, s := range expect.Children {
|
||||||
|
ch, ok := other.Children[name]
|
||||||
|
Assert(
|
||||||
|
ctx,
|
||||||
|
func() bool { return ok },
|
||||||
|
"found matching child container",
|
||||||
|
name,
|
||||||
|
maps.Keys(other.Children))
|
||||||
|
|
||||||
|
AssertEqualTrees(ctx, s, ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,7 +22,7 @@ func Assert(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
header = "Error: " + header
|
header = "TEST FAILURE: " + header
|
||||||
expected := fmt.Sprintf("* Expected: %+v", expect)
|
expected := fmt.Sprintf("* Expected: %+v", expect)
|
||||||
got := fmt.Sprintf("* Current: %+v", current)
|
got := fmt.Sprintf("* Current: %+v", current)
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ func Assert(
|
|||||||
|
|
||||||
func Fatal(ctx context.Context, msg string, err error) {
|
func Fatal(ctx context.Context, msg string, err error) {
|
||||||
logger.CtxErr(ctx, err).Error("test failure: " + msg)
|
logger.CtxErr(ctx, err).Error("test failure: " + msg)
|
||||||
fmt.Println(msg+": ", err)
|
fmt.Println("TEST FAILURE: "+msg+": ", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
src/cmd/sanity_test/export/groups.go
Normal file
16
src/cmd/sanity_test/export/groups.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package export
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
||||||
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckGroupsExport(
|
||||||
|
ctx context.Context,
|
||||||
|
ac api.Client,
|
||||||
|
envs common.Envs,
|
||||||
|
) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
@ -3,28 +3,21 @@ package export
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
|
||||||
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
|
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
||||||
"github.com/alcionai/corso/src/cmd/sanity_test/restore"
|
"github.com/alcionai/corso/src/cmd/sanity_test/restore"
|
||||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CheckOneDriveExport(
|
func CheckOneDriveExport(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *msgraphsdk.GraphServiceClient,
|
ac api.Client,
|
||||||
userID, folderName, dataFolder string,
|
envs common.Envs,
|
||||||
) {
|
) {
|
||||||
drive, err := client.
|
drive, err := ac.Users().GetDefaultDrive(ctx, envs.UserID)
|
||||||
Users().
|
|
||||||
ByUserId(userID).
|
|
||||||
Drive().
|
|
||||||
Get(ctx, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Fatal(ctx, "getting the drive:", err)
|
common.Fatal(ctx, "getting the drive:", err)
|
||||||
}
|
}
|
||||||
@ -36,37 +29,19 @@ func CheckOneDriveExport(
|
|||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
)
|
)
|
||||||
|
|
||||||
err = filepath.Walk(folderName, func(path string, info os.FileInfo, err error) error {
|
err = filepath.Walk(
|
||||||
if err != nil {
|
envs.FolderName,
|
||||||
return clues.Stack(err)
|
common.FilepathWalker(envs.FolderName, exportFileSizes, startTime))
|
||||||
}
|
|
||||||
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
relPath, err := filepath.Rel(folderName, path)
|
|
||||||
if err != nil {
|
|
||||||
return clues.Stack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exportFileSizes[relPath] = info.Size()
|
|
||||||
if startTime.After(info.ModTime()) {
|
|
||||||
startTime = info.ModTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error walking the path:", err)
|
fmt.Println("Error walking the path:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = restore.PopulateDriveDetails(
|
_ = restore.PopulateDriveDetails(
|
||||||
ctx,
|
ctx,
|
||||||
client,
|
ac,
|
||||||
ptr.Val(drive.GetId()),
|
ptr.Val(drive.GetId()),
|
||||||
folderName,
|
envs.FolderName,
|
||||||
dataFolder,
|
envs.DataFolder,
|
||||||
fileSizes,
|
fileSizes,
|
||||||
map[string][]common.PermissionInfo{},
|
map[string][]common.PermissionInfo{},
|
||||||
startTime)
|
startTime)
|
||||||
|
|||||||
@ -3,28 +3,21 @@ package export
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
|
||||||
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
|
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
||||||
"github.com/alcionai/corso/src/cmd/sanity_test/restore"
|
"github.com/alcionai/corso/src/cmd/sanity_test/restore"
|
||||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CheckSharePointExport(
|
func CheckSharePointExport(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *msgraphsdk.GraphServiceClient,
|
ac api.Client,
|
||||||
siteID, folderName, dataFolder string,
|
envs common.Envs,
|
||||||
) {
|
) {
|
||||||
drive, err := client.
|
drive, err := ac.Sites().GetDefaultDrive(ctx, envs.SiteID)
|
||||||
Sites().
|
|
||||||
BySiteId(siteID).
|
|
||||||
Drive().
|
|
||||||
Get(ctx, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Fatal(ctx, "getting the drive:", err)
|
common.Fatal(ctx, "getting the drive:", err)
|
||||||
}
|
}
|
||||||
@ -36,37 +29,19 @@ func CheckSharePointExport(
|
|||||||
startTime = time.Now()
|
startTime = time.Now()
|
||||||
)
|
)
|
||||||
|
|
||||||
err = filepath.Walk(folderName, func(path string, info os.FileInfo, err error) error {
|
err = filepath.Walk(
|
||||||
if err != nil {
|
envs.FolderName,
|
||||||
return clues.Stack(err)
|
common.FilepathWalker(envs.FolderName, exportFileSizes, startTime))
|
||||||
}
|
|
||||||
|
|
||||||
if info.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
relPath, err := filepath.Rel(folderName, path)
|
|
||||||
if err != nil {
|
|
||||||
return clues.Stack(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
exportFileSizes[relPath] = info.Size()
|
|
||||||
if startTime.After(info.ModTime()) {
|
|
||||||
startTime = info.ModTime()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error walking the path:", err)
|
fmt.Println("Error walking the path:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = restore.PopulateDriveDetails(
|
_ = restore.PopulateDriveDetails(
|
||||||
ctx,
|
ctx,
|
||||||
client,
|
ac,
|
||||||
ptr.Val(drive.GetId()),
|
ptr.Val(drive.GetId()),
|
||||||
folderName,
|
envs.FolderName,
|
||||||
dataFolder,
|
envs.DataFolder,
|
||||||
fileSizes,
|
fileSizes,
|
||||||
map[string][]common.PermissionInfo{},
|
map[string][]common.PermissionInfo{},
|
||||||
startTime)
|
startTime)
|
||||||
|
|||||||
@ -3,99 +3,43 @@ package restore
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
stdpath "path"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
|
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/users"
|
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
||||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
"github.com/alcionai/corso/src/pkg/filters"
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckEmailRestoration verifies that the emails count in restored folder is equivalent to
|
// CheckEmailRestoration verifies that the emails count in restored folder is equivalent to
|
||||||
// emails in actual m365 account
|
// emails in actual m365 account
|
||||||
func CheckEmailRestoration(
|
func CheckEmailRestoration(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *msgraphsdk.GraphServiceClient,
|
ac api.Client,
|
||||||
testUser, folderName, dataFolder, baseBackupFolder string,
|
envs common.Envs,
|
||||||
startTime time.Time,
|
|
||||||
) {
|
) {
|
||||||
var (
|
var (
|
||||||
restoreFolder models.MailFolderable
|
folderNameToItemCount = make(map[string]int32)
|
||||||
itemCount = make(map[string]int32)
|
folderNameToRestoreItemCount = make(map[string]int32)
|
||||||
restoreItemCount = make(map[string]int32)
|
|
||||||
builder = client.Users().ByUserId(testUser).MailFolders()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for {
|
restoredTree := buildSanitree(ctx, ac, envs.UserID, envs.FolderName)
|
||||||
result, err := builder.Get(ctx, nil)
|
dataTree := buildSanitree(ctx, ac, envs.UserID, envs.DataFolder)
|
||||||
if err != nil {
|
|
||||||
common.Fatal(ctx, "getting mail folders", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
values := result.GetValue()
|
|
||||||
|
|
||||||
for _, v := range values {
|
|
||||||
itemName := ptr.Val(v.GetDisplayName())
|
|
||||||
|
|
||||||
if itemName == folderName {
|
|
||||||
restoreFolder = v
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if itemName == dataFolder || itemName == baseBackupFolder {
|
|
||||||
// otherwise, recursively aggregate all child folders.
|
|
||||||
getAllMailSubFolders(ctx, client, testUser, v, itemName, dataFolder, itemCount)
|
|
||||||
|
|
||||||
itemCount[itemName] = ptr.Val(v.GetTotalItemCount())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
link, ok := ptr.ValOK(result.GetOdataNextLink())
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
builder = users.NewItemMailFoldersRequestBuilder(link, client.GetAdapter())
|
|
||||||
}
|
|
||||||
|
|
||||||
folderID := ptr.Val(restoreFolder.GetId())
|
|
||||||
folderName = ptr.Val(restoreFolder.GetDisplayName())
|
|
||||||
ctx = clues.Add(
|
ctx = clues.Add(
|
||||||
ctx,
|
ctx,
|
||||||
"restore_folder_id", folderID,
|
"restore_folder_id", restoredTree.ContainerID,
|
||||||
"restore_folder_name", folderName)
|
"restore_folder_name", restoredTree.ContainerName,
|
||||||
|
"original_folder_id", dataTree.ContainerID,
|
||||||
|
"original_folder_name", dataTree.ContainerName)
|
||||||
|
|
||||||
childFolder, err := client.
|
verifyEmailData(ctx, folderNameToRestoreItemCount, folderNameToItemCount)
|
||||||
Users().
|
|
||||||
ByUserId(testUser).
|
|
||||||
MailFolders().
|
|
||||||
ByMailFolderId(folderID).
|
|
||||||
ChildFolders().
|
|
||||||
Get(ctx, nil)
|
|
||||||
if err != nil {
|
|
||||||
common.Fatal(ctx, "getting restore folder child folders", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fld := range childFolder.GetValue() {
|
common.AssertEqualTrees[models.MailFolderable](
|
||||||
restoreDisplayName := ptr.Val(fld.GetDisplayName())
|
ctx,
|
||||||
|
dataTree,
|
||||||
// check if folder is the data folder we loaded or the base backup to verify
|
restoredTree.Children[envs.DataFolder])
|
||||||
// the incremental backup worked fine
|
|
||||||
if strings.EqualFold(restoreDisplayName, dataFolder) || strings.EqualFold(restoreDisplayName, baseBackupFolder) {
|
|
||||||
count, _ := ptr.ValOK(fld.GetTotalItemCount())
|
|
||||||
|
|
||||||
restoreItemCount[restoreDisplayName] = count
|
|
||||||
checkAllSubFolder(ctx, client, fld, testUser, restoreDisplayName, dataFolder, restoreItemCount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyEmailData(ctx, restoreItemCount, itemCount)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyEmailData(ctx context.Context, restoreMessageCount, messageCount map[string]int32) {
|
func verifyEmailData(ctx context.Context, restoreMessageCount, messageCount map[string]int32) {
|
||||||
@ -111,109 +55,71 @@ func verifyEmailData(ctx context.Context, restoreMessageCount, messageCount map[
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAllSubFolder will recursively check for all subfolders and get the corresponding
|
func buildSanitree(
|
||||||
// email count.
|
|
||||||
func getAllMailSubFolders(
|
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *msgraphsdk.GraphServiceClient,
|
ac api.Client,
|
||||||
testUser string,
|
userID, folderName string,
|
||||||
r models.MailFolderable,
|
) *common.Sanitree[models.MailFolderable] {
|
||||||
parentFolder,
|
gcc, err := ac.Mail().GetContainerByName(
|
||||||
dataFolder string,
|
ctx,
|
||||||
messageCount map[string]int32,
|
userID,
|
||||||
) {
|
api.MsgFolderRoot,
|
||||||
var (
|
folderName)
|
||||||
folderID = ptr.Val(r.GetId())
|
|
||||||
count int32 = 99
|
|
||||||
options = &users.ItemMailFoldersItemChildFoldersRequestBuilderGetRequestConfiguration{
|
|
||||||
QueryParameters: &users.ItemMailFoldersItemChildFoldersRequestBuilderGetQueryParameters{
|
|
||||||
Top: &count,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
ctx = clues.Add(ctx, "parent_folder_id", folderID)
|
|
||||||
|
|
||||||
childFolder, err := client.
|
|
||||||
Users().
|
|
||||||
ByUserId(testUser).
|
|
||||||
MailFolders().
|
|
||||||
ByMailFolderId(folderID).
|
|
||||||
ChildFolders().
|
|
||||||
Get(ctx, options)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Fatal(ctx, "getting mail subfolders", err)
|
common.Fatal(
|
||||||
|
ctx,
|
||||||
|
fmt.Sprintf("finding folder by name %q", folderName),
|
||||||
|
err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, child := range childFolder.GetValue() {
|
mmf, ok := gcc.(models.MailFolderable)
|
||||||
var (
|
if !ok {
|
||||||
childDisplayName = ptr.Val(child.GetDisplayName())
|
common.Fatal(
|
||||||
childFolderCount = ptr.Val(child.GetChildFolderCount())
|
ctx,
|
||||||
//nolint:forbidigo
|
"mail folderable required",
|
||||||
fullFolderName = stdpath.Join(parentFolder, childDisplayName)
|
clues.New("casting "+*gcc.GetDisplayName()+" to models.MailFolderable"))
|
||||||
)
|
|
||||||
|
|
||||||
if filters.PathContains([]string{dataFolder}).Compare(fullFolderName) {
|
|
||||||
messageCount[fullFolderName] = ptr.Val(child.GetTotalItemCount())
|
|
||||||
// recursively check for subfolders
|
|
||||||
if childFolderCount > 0 {
|
|
||||||
parentFolder := fullFolderName
|
|
||||||
|
|
||||||
getAllMailSubFolders(ctx, client, testUser, child, parentFolder, dataFolder, messageCount)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
root := &common.Sanitree[models.MailFolderable]{
|
||||||
}
|
Container: mmf,
|
||||||
|
ContainerID: ptr.Val(mmf.GetId()),
|
||||||
// checkAllSubFolder will recursively traverse inside the restore folder and
|
ContainerName: ptr.Val(mmf.GetDisplayName()),
|
||||||
// verify that data matched in all subfolders
|
ContainsItems: int(ptr.Val(mmf.GetTotalItemCount())),
|
||||||
func checkAllSubFolder(
|
Children: map[string]*common.Sanitree[models.MailFolderable]{},
|
||||||
ctx context.Context,
|
}
|
||||||
client *msgraphsdk.GraphServiceClient,
|
|
||||||
r models.MailFolderable,
|
recurseSubfolders(ctx, ac, root, userID)
|
||||||
testUser,
|
|
||||||
parentFolder,
|
return root
|
||||||
dataFolder string,
|
}
|
||||||
restoreMessageCount map[string]int32,
|
|
||||||
) {
|
func recurseSubfolders(
|
||||||
var (
|
ctx context.Context,
|
||||||
folderID = ptr.Val(r.GetId())
|
ac api.Client,
|
||||||
count int32 = 99
|
parent *common.Sanitree[models.MailFolderable],
|
||||||
options = &users.ItemMailFoldersItemChildFoldersRequestBuilderGetRequestConfiguration{
|
userID string,
|
||||||
QueryParameters: &users.ItemMailFoldersItemChildFoldersRequestBuilderGetQueryParameters{
|
) {
|
||||||
Top: &count,
|
childFolders, err := ac.Mail().GetContainerChildren(
|
||||||
},
|
ctx,
|
||||||
}
|
userID,
|
||||||
)
|
parent.ContainerID)
|
||||||
|
if err != nil {
|
||||||
childFolder, err := client.
|
common.Fatal(ctx, "getting subfolders", err)
|
||||||
Users().
|
}
|
||||||
ByUserId(testUser).
|
|
||||||
MailFolders().
|
for _, child := range childFolders {
|
||||||
ByMailFolderId(folderID).
|
c := &common.Sanitree[models.MailFolderable]{
|
||||||
ChildFolders().
|
Container: child,
|
||||||
Get(ctx, options)
|
ContainerID: ptr.Val(child.GetId()),
|
||||||
if err != nil {
|
ContainerName: ptr.Val(child.GetDisplayName()),
|
||||||
common.Fatal(ctx, "getting mail subfolders", err)
|
ContainsItems: int(ptr.Val(child.GetTotalItemCount())),
|
||||||
}
|
Children: map[string]*common.Sanitree[models.MailFolderable]{},
|
||||||
|
}
|
||||||
for _, child := range childFolder.GetValue() {
|
|
||||||
var (
|
parent.Children[c.ContainerName] = c
|
||||||
childDisplayName = ptr.Val(child.GetDisplayName())
|
|
||||||
//nolint:forbidigo
|
if ptr.Val(child.GetChildFolderCount()) > 0 {
|
||||||
fullFolderName = stdpath.Join(parentFolder, childDisplayName)
|
recurseSubfolders(ctx, ac, c, userID)
|
||||||
)
|
|
||||||
|
|
||||||
if filters.PathContains([]string{dataFolder}).Compare(fullFolderName) {
|
|
||||||
childTotalCount, _ := ptr.ValOK(child.GetTotalItemCount())
|
|
||||||
restoreMessageCount[fullFolderName] = childTotalCount
|
|
||||||
}
|
|
||||||
|
|
||||||
childFolderCount := ptr.Val(child.GetChildFolderCount())
|
|
||||||
|
|
||||||
if childFolderCount > 0 {
|
|
||||||
parentFolder := fullFolderName
|
|
||||||
checkAllSubFolder(ctx, client, child, testUser, parentFolder, dataFolder, restoreMessageCount)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/cmd/sanity_test/restore/groups.go
Normal file
16
src/cmd/sanity_test/restore/groups.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package restore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
||||||
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckGroupsRestoration(
|
||||||
|
ctx context.Context,
|
||||||
|
ac api.Client,
|
||||||
|
envs common.Envs,
|
||||||
|
) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
@ -7,12 +7,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
|
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
||||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
"github.com/alcionai/corso/src/pkg/path"
|
"github.com/alcionai/corso/src/pkg/path"
|
||||||
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -21,34 +21,29 @@ const (
|
|||||||
|
|
||||||
func CheckOneDriveRestoration(
|
func CheckOneDriveRestoration(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *msgraphsdk.GraphServiceClient,
|
ac api.Client,
|
||||||
userID, folderName, dataFolder string,
|
envs common.Envs,
|
||||||
startTime time.Time,
|
|
||||||
) {
|
) {
|
||||||
drive, err := client.
|
drive, err := ac.Users().GetDefaultDrive(ctx, envs.UserID)
|
||||||
Users().
|
|
||||||
ByUserId(userID).
|
|
||||||
Drive().
|
|
||||||
Get(ctx, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Fatal(ctx, "getting the drive:", err)
|
common.Fatal(ctx, "getting the drive:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkDriveRestoration(
|
checkDriveRestoration(
|
||||||
ctx,
|
ctx,
|
||||||
client,
|
ac,
|
||||||
path.OneDriveService,
|
path.OneDriveService,
|
||||||
folderName,
|
envs.FolderName,
|
||||||
ptr.Val(drive.GetId()),
|
ptr.Val(drive.GetId()),
|
||||||
ptr.Val(drive.GetName()),
|
ptr.Val(drive.GetName()),
|
||||||
dataFolder,
|
envs.DataFolder,
|
||||||
startTime,
|
envs.StartTime,
|
||||||
false)
|
false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDriveRestoration(
|
func checkDriveRestoration(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *msgraphsdk.GraphServiceClient,
|
ac api.Client,
|
||||||
service path.ServiceType,
|
service path.ServiceType,
|
||||||
folderName,
|
folderName,
|
||||||
driveID,
|
driveID,
|
||||||
@ -70,7 +65,7 @@ func checkDriveRestoration(
|
|||||||
|
|
||||||
restoreFolderID := PopulateDriveDetails(
|
restoreFolderID := PopulateDriveDetails(
|
||||||
ctx,
|
ctx,
|
||||||
client,
|
ac,
|
||||||
driveID,
|
driveID,
|
||||||
folderName,
|
folderName,
|
||||||
dataFolder,
|
dataFolder,
|
||||||
@ -78,7 +73,14 @@ func checkDriveRestoration(
|
|||||||
folderPermissions,
|
folderPermissions,
|
||||||
startTime)
|
startTime)
|
||||||
|
|
||||||
getRestoredDrive(ctx, client, driveID, restoreFolderID, restoreFile, restoredFolderPermissions, startTime)
|
getRestoredDrive(
|
||||||
|
ctx,
|
||||||
|
ac,
|
||||||
|
driveID,
|
||||||
|
restoreFolderID,
|
||||||
|
restoreFile,
|
||||||
|
restoredFolderPermissions,
|
||||||
|
startTime)
|
||||||
|
|
||||||
checkRestoredDriveItemPermissions(
|
checkRestoredDriveItemPermissions(
|
||||||
ctx,
|
ctx,
|
||||||
@ -105,7 +107,7 @@ func checkDriveRestoration(
|
|||||||
|
|
||||||
func PopulateDriveDetails(
|
func PopulateDriveDetails(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *msgraphsdk.GraphServiceClient,
|
ac api.Client,
|
||||||
driveID, folderName, dataFolder string,
|
driveID, folderName, dataFolder string,
|
||||||
fileSizes map[string]int64,
|
fileSizes map[string]int64,
|
||||||
folderPermissions map[string][]common.PermissionInfo,
|
folderPermissions map[string][]common.PermissionInfo,
|
||||||
@ -113,18 +115,12 @@ func PopulateDriveDetails(
|
|||||||
) string {
|
) string {
|
||||||
var restoreFolderID string
|
var restoreFolderID string
|
||||||
|
|
||||||
response, err := client.
|
children, err := ac.Drives().GetFolderChildren(ctx, driveID, "root")
|
||||||
Drives().
|
|
||||||
ByDriveId(driveID).
|
|
||||||
Items().
|
|
||||||
ByDriveItemId("root").
|
|
||||||
Children().
|
|
||||||
Get(ctx, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Fatal(ctx, "getting drive by id", err)
|
common.Fatal(ctx, "getting drive by id", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, driveItem := range response.GetValue() {
|
for _, driveItem := range children {
|
||||||
var (
|
var (
|
||||||
itemID = ptr.Val(driveItem.GetId())
|
itemID = ptr.Val(driveItem.GetId())
|
||||||
itemName = ptr.Val(driveItem.GetName())
|
itemName = ptr.Val(driveItem.GetName())
|
||||||
@ -156,8 +152,17 @@ func PopulateDriveDetails(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
folderPermissions[itemName] = permissionIn(ctx, client, driveID, itemID)
|
folderPermissions[itemName] = permissionIn(ctx, ac, driveID, itemID)
|
||||||
getOneDriveChildFolder(ctx, client, driveID, itemID, itemName, fileSizes, folderPermissions, startTime)
|
|
||||||
|
getOneDriveChildFolder(
|
||||||
|
ctx,
|
||||||
|
ac,
|
||||||
|
driveID,
|
||||||
|
itemID,
|
||||||
|
itemName,
|
||||||
|
fileSizes,
|
||||||
|
folderPermissions,
|
||||||
|
startTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
return restoreFolderID
|
return restoreFolderID
|
||||||
@ -228,18 +233,18 @@ func checkRestoredDriveItemPermissions(
|
|||||||
|
|
||||||
func getOneDriveChildFolder(
|
func getOneDriveChildFolder(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *msgraphsdk.GraphServiceClient,
|
ac api.Client,
|
||||||
driveID, itemID, parentName string,
|
driveID, itemID, parentName string,
|
||||||
fileSizes map[string]int64,
|
fileSizes map[string]int64,
|
||||||
folderPermission map[string][]common.PermissionInfo,
|
folderPermission map[string][]common.PermissionInfo,
|
||||||
startTime time.Time,
|
startTime time.Time,
|
||||||
) {
|
) {
|
||||||
response, err := client.Drives().ByDriveId(driveID).Items().ByDriveItemId(itemID).Children().Get(ctx, nil)
|
children, err := ac.Drives().GetFolderChildren(ctx, driveID, itemID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Fatal(ctx, "getting child folder", err)
|
common.Fatal(ctx, "getting child folder", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, driveItem := range response.GetValue() {
|
for _, driveItem := range children {
|
||||||
var (
|
var (
|
||||||
itemID = ptr.Val(driveItem.GetId())
|
itemID = ptr.Val(driveItem.GetId())
|
||||||
itemName = ptr.Val(driveItem.GetName())
|
itemName = ptr.Val(driveItem.GetName())
|
||||||
@ -268,31 +273,33 @@ func getOneDriveChildFolder(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
folderPermission[fullName] = permissionIn(ctx, client, driveID, itemID)
|
folderPermission[fullName] = permissionIn(ctx, ac, driveID, itemID)
|
||||||
getOneDriveChildFolder(ctx, client, driveID, itemID, fullName, fileSizes, folderPermission, startTime)
|
getOneDriveChildFolder(
|
||||||
|
ctx,
|
||||||
|
ac,
|
||||||
|
driveID,
|
||||||
|
itemID,
|
||||||
|
fullName,
|
||||||
|
fileSizes,
|
||||||
|
folderPermission,
|
||||||
|
startTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRestoredDrive(
|
func getRestoredDrive(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *msgraphsdk.GraphServiceClient,
|
ac api.Client,
|
||||||
driveID, restoreFolderID string,
|
driveID, restoreFolderID string,
|
||||||
restoreFile map[string]int64,
|
restoreFile map[string]int64,
|
||||||
restoreFolder map[string][]common.PermissionInfo,
|
restoreFolder map[string][]common.PermissionInfo,
|
||||||
startTime time.Time,
|
startTime time.Time,
|
||||||
) {
|
) {
|
||||||
restored, err := client.
|
children, err := ac.Drives().GetFolderChildren(ctx, driveID, restoreFolderID)
|
||||||
Drives().
|
|
||||||
ByDriveId(driveID).
|
|
||||||
Items().
|
|
||||||
ByDriveItemId(restoreFolderID).
|
|
||||||
Children().
|
|
||||||
Get(ctx, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Fatal(ctx, "getting child folder", err)
|
common.Fatal(ctx, "getting child folder", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range restored.GetValue() {
|
for _, item := range children {
|
||||||
var (
|
var (
|
||||||
itemID = ptr.Val(item.GetId())
|
itemID = ptr.Val(item.GetId())
|
||||||
itemName = ptr.Val(item.GetName())
|
itemName = ptr.Val(item.GetName())
|
||||||
@ -308,8 +315,16 @@ func getRestoredDrive(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreFolder[itemName] = permissionIn(ctx, client, driveID, itemID)
|
restoreFolder[itemName] = permissionIn(ctx, ac, driveID, itemID)
|
||||||
getOneDriveChildFolder(ctx, client, driveID, itemID, itemName, restoreFile, restoreFolder, startTime)
|
getOneDriveChildFolder(
|
||||||
|
ctx,
|
||||||
|
ac,
|
||||||
|
driveID,
|
||||||
|
itemID,
|
||||||
|
itemName,
|
||||||
|
restoreFile,
|
||||||
|
restoreFolder,
|
||||||
|
startTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,18 +334,12 @@ func getRestoredDrive(
|
|||||||
|
|
||||||
func permissionIn(
|
func permissionIn(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *msgraphsdk.GraphServiceClient,
|
ac api.Client,
|
||||||
driveID, itemID string,
|
driveID, itemID string,
|
||||||
) []common.PermissionInfo {
|
) []common.PermissionInfo {
|
||||||
pi := []common.PermissionInfo{}
|
pi := []common.PermissionInfo{}
|
||||||
|
|
||||||
pcr, err := client.
|
pcr, err := ac.Drives().GetItemPermission(ctx, driveID, itemID)
|
||||||
Drives().
|
|
||||||
ByDriveId(driveID).
|
|
||||||
Items().
|
|
||||||
ByDriveItemId(itemID).
|
|
||||||
Permissions().
|
|
||||||
Get(ctx, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Fatal(ctx, "getting permission", err)
|
common.Fatal(ctx, "getting permission", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,38 +2,31 @@ package restore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
|
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
||||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
"github.com/alcionai/corso/src/pkg/path"
|
"github.com/alcionai/corso/src/pkg/path"
|
||||||
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CheckSharePointRestoration(
|
func CheckSharePointRestoration(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
client *msgraphsdk.GraphServiceClient,
|
ac api.Client,
|
||||||
siteID, userID, folderName, dataFolder string,
|
envs common.Envs,
|
||||||
startTime time.Time,
|
|
||||||
) {
|
) {
|
||||||
drive, err := client.
|
drive, err := ac.Sites().GetDefaultDrive(ctx, envs.SiteID)
|
||||||
Sites().
|
|
||||||
BySiteId(siteID).
|
|
||||||
Drive().
|
|
||||||
Get(ctx, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.Fatal(ctx, "getting the drive:", err)
|
common.Fatal(ctx, "getting the drive:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
checkDriveRestoration(
|
checkDriveRestoration(
|
||||||
ctx,
|
ctx,
|
||||||
client,
|
ac,
|
||||||
path.SharePointService,
|
path.SharePointService,
|
||||||
folderName,
|
envs.FolderName,
|
||||||
ptr.Val(drive.GetId()),
|
ptr.Val(drive.GetId()),
|
||||||
ptr.Val(drive.GetName()),
|
ptr.Val(drive.GetName()),
|
||||||
dataFolder,
|
envs.DataFolder,
|
||||||
startTime,
|
envs.StartTime,
|
||||||
true)
|
true)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,21 +2,40 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/cli/print"
|
||||||
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
"github.com/alcionai/corso/src/cmd/sanity_test/common"
|
||||||
"github.com/alcionai/corso/src/cmd/sanity_test/export"
|
"github.com/alcionai/corso/src/cmd/sanity_test/export"
|
||||||
"github.com/alcionai/corso/src/cmd/sanity_test/restore"
|
"github.com/alcionai/corso/src/cmd/sanity_test/restore"
|
||||||
"github.com/alcionai/corso/src/internal/m365/graph"
|
"github.com/alcionai/corso/src/internal/m365/graph"
|
||||||
"github.com/alcionai/corso/src/internal/tester/tconfig"
|
|
||||||
"github.com/alcionai/corso/src/pkg/logger"
|
"github.com/alcionai/corso/src/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// root command
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func rootCMD() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "sanity-test",
|
||||||
|
Short: "run the sanity tests",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: sanityTestRoot,
|
||||||
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println("running", cmd.UseLine())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanityTestRoot(cmd *cobra.Command, args []string) error {
|
||||||
|
return print.Only(cmd.Context(), clues.New("must specify a kind of test"))
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ls := logger.Settings{
|
ls := logger.Settings{
|
||||||
File: logger.GetLogFile(""),
|
File: logger.GetLogFile(""),
|
||||||
@ -29,60 +48,226 @@ func main() {
|
|||||||
_ = log.Sync() // flush all logs in the buffer
|
_ = log.Sync() // flush all logs in the buffer
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// TODO: only needed for exchange
|
||||||
graph.InitializeConcurrencyLimiter(ctx, true, 4)
|
graph.InitializeConcurrencyLimiter(ctx, true, 4)
|
||||||
|
|
||||||
adapter, err := graph.CreateAdapter(
|
root := rootCMD()
|
||||||
tconfig.GetM365TenantID(ctx),
|
|
||||||
os.Getenv("AZURE_CLIENT_ID"),
|
|
||||||
os.Getenv("AZURE_CLIENT_SECRET"))
|
|
||||||
if err != nil {
|
|
||||||
common.Fatal(ctx, "creating adapter", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
restCMD := restoreCMD()
|
||||||
client = msgraphsdk.NewGraphServiceClient(adapter)
|
|
||||||
testUser = tconfig.GetM365UserID(ctx)
|
|
||||||
testSite = tconfig.GetM365SiteID(ctx)
|
|
||||||
testKind = os.Getenv("SANITY_TEST_KIND") // restore or export (cli arg?)
|
|
||||||
testService = os.Getenv("SANITY_TEST_SERVICE")
|
|
||||||
folder = strings.TrimSpace(os.Getenv("SANITY_TEST_FOLDER"))
|
|
||||||
dataFolder = os.Getenv("TEST_DATA")
|
|
||||||
baseBackupFolder = os.Getenv("BASE_BACKUP")
|
|
||||||
)
|
|
||||||
|
|
||||||
ctx = clues.Add(
|
restCMD.AddCommand(restoreExchangeCMD())
|
||||||
ctx,
|
restCMD.AddCommand(restoreOneDriveCMD())
|
||||||
"resource_owner", testUser,
|
restCMD.AddCommand(restoreSharePointCMD())
|
||||||
"service", testService,
|
restCMD.AddCommand(restoreGroupsCMD())
|
||||||
"sanity_restore_folder", folder)
|
root.AddCommand(restCMD)
|
||||||
|
|
||||||
logger.Ctx(ctx).Info("starting sanity test check")
|
expCMD := exportCMD()
|
||||||
|
|
||||||
switch testKind {
|
expCMD.AddCommand(exportOneDriveCMD())
|
||||||
case "restore":
|
expCMD.AddCommand(exportSharePointCMD())
|
||||||
startTime, _ := common.MustGetTimeFromName(ctx, folder)
|
expCMD.AddCommand(exportGroupsCMD())
|
||||||
clues.Add(ctx, "sanity_restore_start_time", startTime.Format(time.RFC3339))
|
root.AddCommand(expCMD)
|
||||||
|
|
||||||
switch testService {
|
if err := root.Execute(); err != nil {
|
||||||
case "exchange":
|
os.Exit(1)
|
||||||
restore.CheckEmailRestoration(ctx, client, testUser, folder, dataFolder, baseBackupFolder, startTime)
|
|
||||||
case "onedrive":
|
|
||||||
restore.CheckOneDriveRestoration(ctx, client, testUser, folder, dataFolder, startTime)
|
|
||||||
case "sharepoint":
|
|
||||||
restore.CheckSharePointRestoration(ctx, client, testSite, testUser, folder, dataFolder, startTime)
|
|
||||||
default:
|
|
||||||
common.Fatal(ctx, "unknown service for restore sanity tests", nil)
|
|
||||||
}
|
|
||||||
case "export":
|
|
||||||
switch testService {
|
|
||||||
case "onedrive":
|
|
||||||
export.CheckOneDriveExport(ctx, client, testUser, folder, dataFolder)
|
|
||||||
case "sharepoint":
|
|
||||||
export.CheckSharePointExport(ctx, client, testSite, folder, dataFolder)
|
|
||||||
default:
|
|
||||||
common.Fatal(ctx, "unknown service for export sanity tests", nil)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
common.Fatal(ctx, "unknown test kind (expected restore or export)", nil)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// restore/export command
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func exportCMD() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "restore",
|
||||||
|
Short: "run the post-export sanity tests",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: sanityTestExport,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanityTestExport(cmd *cobra.Command, args []string) error {
|
||||||
|
return print.Only(cmd.Context(), clues.New("must specify a service"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreCMD() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "restore",
|
||||||
|
Short: "run the post-restore sanity tests",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: sanityTestRestore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanityTestRestore(cmd *cobra.Command, args []string) error {
|
||||||
|
return print.Only(cmd.Context(), clues.New("must specify a service"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// service commands - export
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func exportGroupsCMD() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "groups",
|
||||||
|
Short: "run the groups export sanity tests",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: sanityTestExportGroups,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanityTestExportGroups(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
envs := common.EnvVars(ctx)
|
||||||
|
|
||||||
|
ac, err := common.GetAC()
|
||||||
|
if err != nil {
|
||||||
|
return print.Only(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
export.CheckGroupsExport(ctx, ac, envs)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func exportOneDriveCMD() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "onedrive",
|
||||||
|
Short: "run the onedrive export sanity tests",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: sanityTestExportOneDrive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanityTestExportOneDrive(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
envs := common.EnvVars(ctx)
|
||||||
|
|
||||||
|
ac, err := common.GetAC()
|
||||||
|
if err != nil {
|
||||||
|
return print.Only(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
export.CheckOneDriveExport(ctx, ac, envs)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func exportSharePointCMD() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "sharepoint",
|
||||||
|
Short: "run the sharepoint export sanity tests",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: sanityTestExportSharePoint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanityTestExportSharePoint(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
envs := common.EnvVars(ctx)
|
||||||
|
|
||||||
|
ac, err := common.GetAC()
|
||||||
|
if err != nil {
|
||||||
|
return print.Only(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
export.CheckSharePointExport(ctx, ac, envs)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// service commands - restore
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func restoreExchangeCMD() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "exchange",
|
||||||
|
Short: "run the exchange restore sanity tests",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: sanityTestRestoreExchange,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanityTestRestoreExchange(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
envs := common.EnvVars(ctx)
|
||||||
|
|
||||||
|
ac, err := common.GetAC()
|
||||||
|
if err != nil {
|
||||||
|
return print.Only(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
restore.CheckEmailRestoration(ctx, ac, envs)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreOneDriveCMD() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "onedrive",
|
||||||
|
Short: "run the onedrive restore sanity tests",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: sanityTestRestoreOneDrive,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanityTestRestoreOneDrive(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
envs := common.EnvVars(ctx)
|
||||||
|
|
||||||
|
ac, err := common.GetAC()
|
||||||
|
if err != nil {
|
||||||
|
return print.Only(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
restore.CheckOneDriveRestoration(ctx, ac, envs)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreSharePointCMD() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "sharepoint",
|
||||||
|
Short: "run the sharepoint restore sanity tests",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: sanityTestRestoreSharePoint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanityTestRestoreSharePoint(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
envs := common.EnvVars(ctx)
|
||||||
|
|
||||||
|
ac, err := common.GetAC()
|
||||||
|
if err != nil {
|
||||||
|
return print.Only(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
restore.CheckSharePointRestoration(ctx, ac, envs)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func restoreGroupsCMD() *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "groups",
|
||||||
|
Short: "run the groups restore sanity tests",
|
||||||
|
DisableAutoGenTag: true,
|
||||||
|
RunE: sanityTestRestoreGroups,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sanityTestRestoreGroups(cmd *cobra.Command, args []string) error {
|
||||||
|
ctx := cmd.Context()
|
||||||
|
envs := common.EnvVars(ctx)
|
||||||
|
|
||||||
|
ac, err := common.GetAC()
|
||||||
|
if err != nil {
|
||||||
|
return print.Only(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
restore.CheckGroupsRestoration(ctx, ac, envs)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import (
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
Credentials account.M365Config
|
Credentials account.M365Config
|
||||||
|
|
||||||
// The Stable service is re-usable for any non-paged request.
|
// The Stable service is re-usable for any request.
|
||||||
// This allows us to maintain performance across async requests.
|
// This allows us to maintain performance across async requests.
|
||||||
Stable graph.Servicer
|
Stable graph.Servicer
|
||||||
|
|
||||||
|
|||||||
@ -84,6 +84,26 @@ func (c Drives) GetRootFolder(
|
|||||||
return root, nil
|
return root, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: pagination controller needed for completion.
|
||||||
|
func (c Drives) GetFolderChildren(
|
||||||
|
ctx context.Context,
|
||||||
|
driveID, folderID string,
|
||||||
|
) ([]models.DriveItemable, error) {
|
||||||
|
response, err := c.Stable.
|
||||||
|
Client().
|
||||||
|
Drives().
|
||||||
|
ByDriveId(driveID).
|
||||||
|
Items().
|
||||||
|
ByDriveItemId(folderID).
|
||||||
|
Children().
|
||||||
|
Get(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, graph.Wrap(ctx, err, "getting folder children")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.GetValue(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Items
|
// Items
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
@ -223,6 +223,26 @@ func (c Mail) PatchFolder(
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: needs pager implementation for completion
|
||||||
|
func (c Mail) GetContainerChildren(
|
||||||
|
ctx context.Context,
|
||||||
|
userID, containerID string,
|
||||||
|
) ([]models.MailFolderable, error) {
|
||||||
|
resp, err := c.Stable.
|
||||||
|
Client().
|
||||||
|
Users().
|
||||||
|
ByUserId(userID).
|
||||||
|
MailFolders().
|
||||||
|
ByMailFolderId(containerID).
|
||||||
|
ChildFolders().
|
||||||
|
Get(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, graph.Wrap(ctx, err, "getting container child folders")
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.GetValue(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// items
|
// items
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user