Compare commits

...

4 Commits

Author SHA1 Message Date
Ashlie Martinez
42d0e5d196 Create tests showing when caching is used
Add kopia tests that show what fields can be changed to cause a file to
be marked as uncached.
2023-02-01 16:30:54 -08:00
Ashlie Martinez
070622fd08 Fixup test calls that create StreamingFiles 2023-02-01 16:30:16 -08:00
Ashlie Martinez
0928e53a8b Add interface to get version of item
Pass version as UID in OwnerInfo to kopia to allow more control of what
kopia thinks is a cached file.
2023-02-01 16:29:32 -08:00
Ashlie Martinez
41d5550b4b Use fork of kopia with OwnerInfo patch 2023-02-01 16:29:04 -08:00
7 changed files with 292 additions and 10 deletions

View File

@ -2,6 +2,8 @@ module github.com/alcionai/corso/src
go 1.19
replace github.com/kopia/kopia => github.com/alcionai/kopia v0.12.2-0.20230202002134-2a25d14bb364
require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0
github.com/alcionai/clues v0.0.0-20230131232239-cee86233b005
@ -80,13 +82,13 @@ require (
github.com/klauspost/reedsolomon v1.11.3 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-isatty v0.0.17 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/microsoft/kiota-serialization-text-go v0.6.0
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.45 // indirect
github.com/minio/minio-go/v7 v7.0.47 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@ -117,7 +119,7 @@ require (
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.4.0 // indirect
golang.org/x/text v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
google.golang.org/grpc v1.52.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect

View File

@ -54,6 +54,8 @@ github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpH
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/alcionai/clues v0.0.0-20230131232239-cee86233b005 h1:eTgICcmcydEWG8J+hgnidf0pzujV3Gd2XqmknykZkzA=
github.com/alcionai/clues v0.0.0-20230131232239-cee86233b005/go.mod h1:UlAs8jkWIpsOMakiC8NxPgQQVQRdvyf1hYMszlYYLb4=
github.com/alcionai/kopia v0.12.2-0.20230202002134-2a25d14bb364 h1:mJ3zLxz88hrbALYff9D4/SI/97hVRiRFeMLUz7gcy8Q=
github.com/alcionai/kopia v0.12.2-0.20230202002134-2a25d14bb364/go.mod h1:f5sHcTthFWc16s0maScUqaK6JPLVxCqF8Pwz4iIZaOE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -241,8 +243,6 @@ github.com/klauspost/reedsolomon v1.11.3/go.mod h1:FXLZzlJIdfqEnQLdUKWNRuMZg747h
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kopia/htmluibuild v0.0.0-20220928042710-9fdd02afb1e7 h1:WP5VfIQL7AaYkO4zTNSCsVOawTzudbc4tvLojvg0RKc=
github.com/kopia/kopia v0.12.2-0.20230123092305-e5387cec0acb h1:0jLaKLiloYvRNbuHHpnQkJ7STAgzQ4z6n+KPa6Kyg7I=
github.com/kopia/kopia v0.12.2-0.20230123092305-e5387cec0acb/go.mod h1:dtCyMCsWulG82o9bDopvnny9DpOQe0PnSDczJLuhnWA=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@ -259,8 +259,9 @@ github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@ -287,8 +288,8 @@ github.com/microsoftgraph/msgraph-sdk-go-core v0.33.0 h1:cDL3ov/IZ2ZarUJdGGPsdR+
github.com/microsoftgraph/msgraph-sdk-go-core v0.33.0/go.mod h1:d0mU3PQAWnN/C4CwPJEZz2QhesrnR5UDnqRu2ODWPkI=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.45 h1:g4IeM9M9pW/Lo8AGGNOjBZYlvmtlE1N5TQEYWXRWzIs=
github.com/minio/minio-go/v7 v7.0.45/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw=
github.com/minio/minio-go/v7 v7.0.47 h1:sLiuCKGSIcn/MI6lREmTzX91DX/oRau4ia0j6e6eOSs=
github.com/minio/minio-go/v7 v7.0.47/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@ -747,8 +748,8 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef h1:uQ2vjV/sHTsWSqdKeLqmwitzgvjMl7o4IdtHwUDXSJY=
google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w=
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=

View File

@ -20,6 +20,7 @@ type MockExchangeDataCollection struct {
Data [][]byte
Names []string
ModTimes []time.Time
Versions []uint32
ColState data.CollectionState
PrevPath path.Path
DeletedItems []bool
@ -31,6 +32,7 @@ var (
_ data.Stream = &MockExchangeData{}
_ data.StreamInfo = &MockExchangeData{}
_ data.StreamSize = &MockExchangeData{}
_ data.Versioner = &MockExchangeData{}
)
// NewMockExchangeDataCollection creates an data collection that will return the specified number of
@ -54,6 +56,8 @@ func NewMockExchangeCollection(pathRepresentation path.Path, numMessagesToReturn
c.DeletedItems = append(c.DeletedItems, false)
}
c.Versions = make([]uint32, c.messageCount)
return c
}
@ -124,6 +128,7 @@ func (medc *MockExchangeDataCollection) Items() <-chan data.Stream {
size: int64(len(medc.Data[i])),
modifiedTime: medc.ModTimes[i],
deleted: medc.DeletedItems[i],
version: medc.Versions[i],
}
}
}()
@ -139,6 +144,7 @@ type MockExchangeData struct {
size int64
modifiedTime time.Time
deleted bool
version uint32
}
func (med *MockExchangeData) UUID() string {
@ -175,6 +181,10 @@ func (med *MockExchangeData) ModTime() time.Time {
return med.modifiedTime
}
func (med *MockExchangeData) Version() uint32 {
return med.version
}
type errReader struct {
readErr error
}

View File

@ -87,6 +87,10 @@ type StreamModTime interface {
ModTime() time.Time
}
type Versioner interface {
Version() uint32
}
// ------------------------------------------------------------------------------------------------
// functionality
// ------------------------------------------------------------------------------------------------

View File

@ -339,9 +339,15 @@ func collectionEntries(
modTime = smt.ModTime()
}
o := fs.OwnerInfo{}
if sv, ok := e.(data.Versioner); ok {
o.UserID = sv.Version()
}
entry := virtualfs.StreamingFileWithModTimeFromReader(
encodedName,
modTime,
o,
newBackupStreamReader(serializationVersion, e.ToReader()),
)
if err := cb(ctx, entry); err != nil {

View File

@ -1027,6 +1027,7 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeSingleSubtree() {
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(testFileName)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(testFileData)),
),
},
@ -1334,6 +1335,7 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeMultipleSubdirecto
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(inboxFileName1)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(inboxFileData1)),
),
virtualfs.NewStaticDirectory(
@ -1342,11 +1344,13 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeMultipleSubdirecto
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(personalFileName1)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(testFileData)),
),
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(personalFileName2)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(testFileData2)),
),
},
@ -1357,6 +1361,7 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeMultipleSubdirecto
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(workFileName1)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(testFileData3)),
),
},
@ -2018,6 +2023,7 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeSkipsDeletedSubtre
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(testFileName)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(testFileData)),
),
},
@ -2028,6 +2034,7 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeSkipsDeletedSubtre
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(testFileName2)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(testFileData2)),
),
},
@ -2043,6 +2050,7 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeSkipsDeletedSubtre
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(testFileName3)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(testFileData3)),
),
},
@ -2053,6 +2061,7 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeSkipsDeletedSubtre
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(testFileName4)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(testFileData4)),
),
},
@ -2201,6 +2210,7 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeSelectsCorrectSubt
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(inboxFileName1)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(inboxFileData1)),
),
},
@ -2216,6 +2226,7 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeSelectsCorrectSubt
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(contactsFileName1)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(contactsFileData1)),
),
},
@ -2255,6 +2266,7 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeSelectsCorrectSubt
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(inboxFileName1)[0],
time.Time{},
fs.OwnerInfo{},
// Wrap with a backup reader so it gets the version injected.
newBackupStreamReader(
serializationVersion,
@ -2274,6 +2286,7 @@ func (suite *HierarchyBuilderUnitSuite) TestBuildDirectoryTreeSelectsCorrectSubt
virtualfs.StreamingFileWithModTimeFromReader(
encodeElements(eventsFileName1)[0],
time.Time{},
fs.OwnerInfo{},
io.NopCloser(bytes.NewReader(eventsFileData1)),
),
},

View File

@ -6,6 +6,7 @@ import (
"io"
stdpath "path"
"testing"
"time"
"github.com/google/uuid"
"github.com/kopia/kopia/repo"
@ -510,6 +511,251 @@ func (suite *KopiaIntegrationSuite) TestBackupCollectionsHandlesNoCollections()
}
}
type KopiaCachedBackupSuite struct {
suite.Suite
w *Wrapper
ctx context.Context
snapshotBase IncrementalBase
testPath path.Path
baseFileName string
baseModTime time.Time
baseVersion uint32
tags map[string]string
expectedTags map[string]string
}
func TestKopiaCachedBackupSuite(t *testing.T) {
tester.RunOnAny(
t,
tester.CorsoCITests,
tester.CorsoKopiaWrapperTests)
suite.Run(t, new(KopiaCachedBackupSuite))
}
func (suite *KopiaCachedBackupSuite) SetupSuite() {
tester.MustGetEnvSets(suite.T(), tester.AWSStorageCredEnvs)
tmp, err := path.Builder{}.Append(testInboxDir).ToDataLayerExchangePathForCategory(
testTenant,
testUser,
path.EmailCategory,
false,
)
require.NoError(suite.T(), err)
suite.testPath = tmp
// tags that are supplied by the caller. This includes basic tags to support
// lookups and extra tags the caller may want to apply.
suite.tags = map[string]string{
"fnords": "smarf",
"brunhilda": "",
}
reasons := []Reason{
{
ResourceOwner: suite.testPath.ResourceOwner(),
Service: suite.testPath.Service(),
Category: suite.testPath.Category(),
},
}
for _, r := range reasons {
for _, k := range r.TagKeys() {
suite.tags[k] = ""
}
}
suite.expectedTags = map[string]string{}
maps.Copy(suite.expectedTags, normalizeTagKVs(suite.tags))
}
func (suite *KopiaCachedBackupSuite) SetupTest() {
t := suite.T()
//nolint:forbidigo
suite.ctx, _ = logger.SeedLevel(context.Background(), logger.Development)
c, err := openKopiaRepo(t, suite.ctx)
require.NoError(t, err)
suite.w = &Wrapper{c}
baseCollection := mockconnector.NewMockExchangeCollection(suite.testPath, 1)
suite.baseFileName = baseCollection.Names[0]
suite.baseModTime = baseCollection.ModTimes[0]
suite.baseVersion = baseCollection.Versions[0]
// Make a backup to start with as the base.
stats, deets, _, err := suite.w.BackupCollections(
suite.ctx,
nil,
[]data.Collection{baseCollection},
nil,
suite.tags,
false,
)
assert.NoError(t, err)
assert.Equal(suite.T(), 1, stats.TotalFileCount, "total files")
assert.Equal(suite.T(), 1, stats.UncachedFileCount, "uncached files")
assert.Equal(suite.T(), 0, stats.CachedFileCount, "cached files")
assert.Equal(suite.T(), 5, stats.TotalDirectoryCount)
assert.Equal(suite.T(), 0, stats.IgnoredErrorCount)
assert.Equal(suite.T(), 0, stats.ErrorCount)
assert.False(suite.T(), stats.Incomplete)
// 1 file and 5 folder entries.
details := deets.Details().Entries
assert.Len(
suite.T(),
details,
6,
)
for _, entry := range details {
assert.True(suite.T(), entry.Updated)
}
checkSnapshotTags(
t,
suite.ctx,
suite.w.c,
suite.expectedTags,
stats.SnapshotID,
)
snap, err := snapshot.LoadSnapshot(
suite.ctx,
suite.w.c,
manifest.ID(stats.SnapshotID),
)
require.NoError(t, err)
suite.snapshotBase = IncrementalBase{
Manifest: snap,
}
}
func (suite *KopiaCachedBackupSuite) TearDownTest() {
assert.NoError(suite.T(), suite.w.Close(suite.ctx))
logger.Flush(suite.ctx)
}
func (suite *KopiaCachedBackupSuite) TestBackupCollections_WithModTimeAndVersion() {
table := []struct {
name string
col func() data.Collection
expectedUploadedFiles int
expectedCachedFiles int
// Whether entries in the resulting details should be marked as updated.
deetsUpdated bool
}{
{
name: "NoUpdates",
col: func() data.Collection {
res := mockconnector.NewMockExchangeCollection(suite.testPath, 1)
res.Names[0] = suite.baseFileName
res.ModTimes[0] = suite.baseModTime
res.Versions[0] = suite.baseVersion
return res
},
expectedUploadedFiles: 0,
expectedCachedFiles: 1,
deetsUpdated: false,
},
{
name: "UpdatedModTime",
col: func() data.Collection {
res := mockconnector.NewMockExchangeCollection(suite.testPath, 1)
res.Names[0] = suite.baseFileName
res.ModTimes[0] = suite.baseModTime.Add(1 * time.Minute)
res.Versions[0] = suite.baseVersion
return res
},
expectedUploadedFiles: 1,
expectedCachedFiles: 0,
deetsUpdated: true,
},
{
name: "UpdatedVersion",
col: func() data.Collection {
res := mockconnector.NewMockExchangeCollection(suite.testPath, 1)
res.Names[0] = suite.baseFileName
res.ModTimes[0] = suite.baseModTime
res.Versions[0] = suite.baseVersion + 1
return res
},
expectedUploadedFiles: 1,
expectedCachedFiles: 0,
deetsUpdated: true,
},
{
name: "UpdatedModTimeAndVersion",
col: func() data.Collection {
res := mockconnector.NewMockExchangeCollection(suite.testPath, 1)
res.Names[0] = suite.baseFileName
res.ModTimes[0] = suite.baseModTime.Add(1 * time.Minute)
res.Versions[0] = suite.baseVersion + 1
return res
},
expectedUploadedFiles: 1,
expectedCachedFiles: 0,
deetsUpdated: true,
},
}
for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) {
stats, deets, _, err := suite.w.BackupCollections(
suite.ctx,
[]IncrementalBase{suite.snapshotBase},
[]data.Collection{test.col()},
nil,
suite.tags,
false,
)
assert.NoError(t, err)
assert.Equal(t, test.expectedUploadedFiles, stats.TotalFileCount, "total files")
assert.Equal(t, test.expectedUploadedFiles, stats.UncachedFileCount, "uncached files")
assert.Equal(t, test.expectedCachedFiles, stats.CachedFileCount, "cached files")
assert.Equal(t, 5, stats.TotalDirectoryCount)
assert.Equal(t, 0, stats.IgnoredErrorCount)
assert.Equal(t, 0, stats.ErrorCount)
assert.False(t, stats.Incomplete)
// 1 file and 5 folder entries.
details := deets.Details().Entries
assert.Len(
t,
details,
test.expectedUploadedFiles+test.expectedCachedFiles+5,
)
for _, entry := range details {
assert.Equal(t, test.deetsUpdated, entry.Updated)
}
checkSnapshotTags(
t,
suite.ctx,
suite.w.c,
suite.expectedTags,
stats.SnapshotID,
)
})
}
}
type KopiaSimpleRepoIntegrationSuite struct {
suite.Suite
w *Wrapper