@@ -20,6 +20,7 @@ import (
2020
2121 sgbucket "github.com/couchbase/sg-bucket"
2222 "github.com/couchbase/sync_gateway/base"
23+ "github.com/couchbase/sync_gateway/channels"
2324 "github.com/stretchr/testify/assert"
2425 "github.com/stretchr/testify/require"
2526)
@@ -1957,3 +1958,181 @@ func TestPutExistingCurrentVersionWithNoExistingDoc(t *testing.T) {
19571958 assert .True (t , reflect .DeepEqual (syncData .HLV .PreviousVersions , pv ))
19581959 assert .Equal (t , "1-3a208ea66e84121b528f05b5457d1134" , syncData .CurrentRev )
19591960}
1961+
1962+ // TestGetCVWithDocResidentInCache:
1963+ // - Two test cases, one with doc a user will have access to, one without
1964+ // - Purpose is to have a doc that is resident in rev cache and use the GetCV function to retrieve these docs
1965+ // - Assert that the doc the user has access to is corrected fetched
1966+ // - Assert the doc the user doesn't have access to is fetched but correctly redacted
1967+ func TestGetCVWithDocResidentInCache (t * testing.T ) {
1968+ const docID = "doc1"
1969+
1970+ testCases := []struct {
1971+ name string
1972+ docChannels []string
1973+ access bool
1974+ }{
1975+ {
1976+ name : "getCVWithUserAccess" ,
1977+ docChannels : []string {"A" },
1978+ access : true ,
1979+ },
1980+ {
1981+ name : "getCVWithoutUserAccess" ,
1982+ docChannels : []string {"B" },
1983+ access : false ,
1984+ },
1985+ }
1986+ for _ , testCase := range testCases {
1987+ t .Run (testCase .name , func (t * testing.T ) {
1988+ db , ctx := setupTestDB (t )
1989+ defer db .Close (ctx )
1990+ collection , ctx := GetSingleDatabaseCollectionWithUser (ctx , t , db )
1991+ collection .ChannelMapper = channels .NewChannelMapper (ctx , channels .DocChannelsSyncFunction , db .Options .JavascriptTimeout )
1992+
1993+ // Create a user with access to channel A
1994+ authenticator := db .Authenticator (base .TestCtx (t ))
1995+ user , err := authenticator .NewUser ("alice" , "letmein" , channels .BaseSetOf (t , "A" ))
1996+ require .NoError (t , err )
1997+ require .NoError (t , authenticator .Save (user ))
1998+ collection .user , err = authenticator .GetUser ("alice" )
1999+ require .NoError (t , err )
2000+
2001+ // create doc with the channels for the test case
2002+ docBody := Body {"channels" : testCase .docChannels }
2003+ rev , doc , err := collection .Put (ctx , docID , docBody )
2004+ require .NoError (t , err )
2005+
2006+ vrs := doc .HLV .Version
2007+ src := doc .HLV .SourceID
2008+ sv := & Version {Value : vrs , SourceID : src }
2009+ revision , err := collection .GetCV (ctx , docID , sv , true )
2010+ require .NoError (t , err )
2011+ if testCase .access {
2012+ assert .Equal (t , rev , revision .RevID )
2013+ assert .Equal (t , sv , revision .CV )
2014+ assert .Equal (t , docID , revision .DocID )
2015+ assert .Equal (t , []byte (`{"channels":["A"]}` ), revision .BodyBytes )
2016+ } else {
2017+ assert .Equal (t , rev , revision .RevID )
2018+ assert .Equal (t , sv , revision .CV )
2019+ assert .Equal (t , docID , revision .DocID )
2020+ assert .Equal (t , []byte (RemovedRedactedDocument ), revision .BodyBytes )
2021+ }
2022+ })
2023+ }
2024+ }
2025+
2026+ // TestGetByCVForDocNotResidentInCache:
2027+ // - Setup db with rev cache size of 1
2028+ // - Put two docs forcing eviction of the first doc
2029+ // - Use GetCV function to fetch the first doc, forcing the rev cache to load the doc from bucket
2030+ // - Assert the doc revision fetched is correct to the first doc we created
2031+ func TestGetByCVForDocNotResidentInCache (t * testing.T ) {
2032+ db , ctx := SetupTestDBWithOptions (t , DatabaseContextOptions {
2033+ RevisionCacheOptions : & RevisionCacheOptions {
2034+ Size : 1 ,
2035+ },
2036+ })
2037+ defer db .Close (ctx )
2038+ collection , ctx := GetSingleDatabaseCollectionWithUser (ctx , t , db )
2039+ collection .ChannelMapper = channels .NewChannelMapper (ctx , channels .DocChannelsSyncFunction , db .Options .JavascriptTimeout )
2040+
2041+ // Create a user with access to channel A
2042+ authenticator := db .Authenticator (base .TestCtx (t ))
2043+ user , err := authenticator .NewUser ("alice" , "letmein" , channels .BaseSetOf (t , "A" ))
2044+ require .NoError (t , err )
2045+ require .NoError (t , authenticator .Save (user ))
2046+ collection .user , err = authenticator .GetUser ("alice" )
2047+ require .NoError (t , err )
2048+
2049+ const (
2050+ doc1ID = "doc1"
2051+ doc2ID = "doc2"
2052+ )
2053+
2054+ revBody := Body {"channels" : []string {"A" }}
2055+ rev , doc , err := collection .Put (ctx , doc1ID , revBody )
2056+ require .NoError (t , err )
2057+
2058+ // put another doc that should evict first doc from cache
2059+ _ , _ , err = collection .Put (ctx , doc2ID , revBody )
2060+ require .NoError (t , err )
2061+
2062+ // get by CV should force a load from bucket and have a cache miss
2063+ vrs := doc .HLV .Version
2064+ src := doc .HLV .SourceID
2065+ sv := & Version {Value : vrs , SourceID : src }
2066+ revision , err := collection .GetCV (ctx , doc1ID , sv , true )
2067+ require .NoError (t , err )
2068+
2069+ // assert the fetched doc is the first doc we added and assert that we did in fact get cache miss
2070+ assert .Equal (t , int64 (1 ), db .DbStats .Cache ().RevisionCacheMisses .Value ())
2071+ assert .Equal (t , rev , revision .RevID )
2072+ assert .Equal (t , sv , revision .CV )
2073+ assert .Equal (t , doc1ID , revision .DocID )
2074+ assert .Equal (t , []byte (`{"channels":["A"]}` ), revision .BodyBytes )
2075+ }
2076+
2077+ // TestGetCVActivePathway:
2078+ // - Two test cases, one with doc a user will have access to, one without
2079+ // - Purpose is top specify nil CV to the GetCV function to force the GetActive code pathway
2080+ // - Assert doc that is created is fetched correctly when user has access to doc
2081+ // - Assert that correct error is returned when user has no access to the doc
2082+ func TestGetCVActivePathway (t * testing.T ) {
2083+ const docID = "doc1"
2084+
2085+ testCases := []struct {
2086+ name string
2087+ docChannels []string
2088+ access bool
2089+ }{
2090+ {
2091+ name : "activeFetchWithUserAccess" ,
2092+ docChannels : []string {"A" },
2093+ access : true ,
2094+ },
2095+ {
2096+ name : "activeFetchWithoutUserAccess" ,
2097+ docChannels : []string {"B" },
2098+ access : false ,
2099+ },
2100+ }
2101+ for _ , testCase := range testCases {
2102+ t .Run (testCase .name , func (t * testing.T ) {
2103+ db , ctx := setupTestDB (t )
2104+ defer db .Close (ctx )
2105+ collection , ctx := GetSingleDatabaseCollectionWithUser (ctx , t , db )
2106+ collection .ChannelMapper = channels .NewChannelMapper (ctx , channels .DocChannelsSyncFunction , db .Options .JavascriptTimeout )
2107+
2108+ // Create a user with access to channel A
2109+ authenticator := db .Authenticator (base .TestCtx (t ))
2110+ user , err := authenticator .NewUser ("alice" , "letmein" , channels .BaseSetOf (t , "A" ))
2111+ require .NoError (t , err )
2112+ require .NoError (t , authenticator .Save (user ))
2113+ collection .user , err = authenticator .GetUser ("alice" )
2114+ require .NoError (t , err )
2115+
2116+ // test get active path by specifying nil cv
2117+ revBody := Body {"channels" : testCase .docChannels }
2118+ rev , doc , err := collection .Put (ctx , docID , revBody )
2119+ require .NoError (t , err )
2120+ revision , err := collection .GetCV (ctx , docID , nil , true )
2121+
2122+ if testCase .access == true {
2123+ require .NoError (t , err )
2124+ vrs := doc .HLV .Version
2125+ src := doc .HLV .SourceID
2126+ sv := & Version {Value : vrs , SourceID : src }
2127+ assert .Equal (t , rev , revision .RevID )
2128+ assert .Equal (t , sv , revision .CV )
2129+ assert .Equal (t , docID , revision .DocID )
2130+ assert .Equal (t , []byte (`{"channels":["A"]}` ), revision .BodyBytes )
2131+ } else {
2132+ require .Error (t , err )
2133+ assert .ErrorContains (t , err , ErrForbidden .Error ())
2134+ assert .Equal (t , DocumentRevision {}, revision )
2135+ }
2136+ })
2137+ }
2138+ }
0 commit comments