2022-11-30 13:42:42 -08:00
package entity_server_tests
2022-09-30 23:56:07 +04:00
import (
2022-11-01 17:33:36 -07:00
"encoding/json"
2022-09-30 23:56:07 +04:00
"fmt"
"reflect"
"testing"
"time"
2022-11-01 08:28:13 -07:00
"github.com/grafana/grafana/pkg/models"
2022-11-04 09:37:25 -07:00
"github.com/grafana/grafana/pkg/services/store"
2022-11-30 13:42:42 -08:00
"github.com/grafana/grafana/pkg/services/store/entity"
2022-11-01 17:33:36 -07:00
"github.com/grafana/grafana/pkg/util"
2022-09-30 23:56:07 +04:00
"github.com/stretchr/testify/require"
"google.golang.org/grpc/metadata"
)
2022-11-30 13:42:42 -08:00
type rawEntityMatcher struct {
grn * entity . GRN
2022-10-05 11:58:46 -07:00
createdRange [ ] time . Time
updatedRange [ ] time . Time
2022-10-05 18:46:17 -07:00
createdBy string
updatedBy string
2022-10-05 11:58:46 -07:00
body [ ] byte
version * string
2022-10-04 11:57:26 -07:00
}
type objectVersionMatcher struct {
2022-10-05 11:58:46 -07:00
updatedRange [ ] time . Time
2022-10-05 18:46:17 -07:00
updatedBy string
2022-10-05 11:58:46 -07:00
version * string
etag * string
comment * string
2022-09-30 23:56:07 +04:00
}
func timestampInRange ( ts int64 , tsRange [ ] time . Time ) bool {
2022-11-01 17:33:36 -07:00
low := tsRange [ 0 ] . UnixMilli ( ) - 1
high := tsRange [ 1 ] . UnixMilli ( ) + 1
return ts >= low && ts <= high
2022-09-30 23:56:07 +04:00
}
2022-11-30 13:42:42 -08:00
func requireEntityMatch ( t * testing . T , obj * entity . Entity , m rawEntityMatcher ) {
2022-09-30 23:56:07 +04:00
t . Helper ( )
2022-10-04 11:57:26 -07:00
require . NotNil ( t , obj )
2022-09-30 23:56:07 +04:00
mismatches := ""
2022-11-01 17:33:36 -07:00
if m . grn != nil {
if m . grn . TenantId > 0 && m . grn . TenantId != obj . GRN . TenantId {
mismatches += fmt . Sprintf ( "expected tenant: %d, actual: %d\n" , m . grn . TenantId , obj . GRN . TenantId )
}
if m . grn . Kind != "" && m . grn . Kind != obj . GRN . Kind {
mismatches += fmt . Sprintf ( "expected Kind: %s, actual: %s\n" , m . grn . Kind , obj . GRN . Kind )
}
if m . grn . UID != "" && m . grn . UID != obj . GRN . UID {
mismatches += fmt . Sprintf ( "expected UID: %s, actual: %s\n" , m . grn . UID , obj . GRN . UID )
}
2022-09-30 23:56:07 +04:00
}
2022-11-08 14:42:32 -08:00
if len ( m . createdRange ) == 2 && ! timestampInRange ( obj . CreatedAt , m . createdRange ) {
mismatches += fmt . Sprintf ( "expected Created range: [from %s to %s], actual created: %s\n" , m . createdRange [ 0 ] , m . createdRange [ 1 ] , time . UnixMilli ( obj . CreatedAt ) )
2022-09-30 23:56:07 +04:00
}
2022-11-08 14:42:32 -08:00
if len ( m . updatedRange ) == 2 && ! timestampInRange ( obj . UpdatedAt , m . updatedRange ) {
mismatches += fmt . Sprintf ( "expected Updated range: [from %s to %s], actual updated: %s\n" , m . updatedRange [ 0 ] , m . updatedRange [ 1 ] , time . UnixMilli ( obj . UpdatedAt ) )
2022-09-30 23:56:07 +04:00
}
2022-10-05 18:46:17 -07:00
if m . createdBy != "" && m . createdBy != obj . CreatedBy {
mismatches += fmt . Sprintf ( "createdBy: expected:%s, found:%s\n" , m . createdBy , obj . CreatedBy )
2022-09-30 23:56:07 +04:00
}
2022-10-05 18:46:17 -07:00
if m . updatedBy != "" && m . updatedBy != obj . UpdatedBy {
mismatches += fmt . Sprintf ( "updatedBy: expected:%s, found:%s\n" , m . updatedBy , obj . UpdatedBy )
2022-09-30 23:56:07 +04:00
}
2022-11-01 17:33:36 -07:00
if len ( m . body ) > 0 {
if json . Valid ( m . body ) {
require . JSONEq ( t , string ( m . body ) , string ( obj . Body ) , "expecting same body" )
} else if ! reflect . DeepEqual ( m . body , obj . Body ) {
mismatches += fmt . Sprintf ( "expected body len: %d, actual body len: %d\n" , len ( m . body ) , len ( obj . Body ) )
}
2022-09-30 23:56:07 +04:00
}
if m . version != nil && * m . version != obj . Version {
mismatches += fmt . Sprintf ( "expected version: %s, actual version: %s\n" , * m . version , obj . Version )
}
2022-10-04 11:57:26 -07:00
require . True ( t , len ( mismatches ) == 0 , mismatches )
}
2022-11-30 13:42:42 -08:00
func requireVersionMatch ( t * testing . T , obj * entity . EntityVersionInfo , m objectVersionMatcher ) {
2022-10-04 11:57:26 -07:00
t . Helper ( )
mismatches := ""
if m . etag != nil && * m . etag != obj . ETag {
mismatches += fmt . Sprintf ( "expected etag: %s, actual etag: %s\n" , * m . etag , obj . ETag )
}
2022-11-08 14:42:32 -08:00
if len ( m . updatedRange ) == 2 && ! timestampInRange ( obj . UpdatedAt , m . updatedRange ) {
mismatches += fmt . Sprintf ( "expected updatedRange range: [from %s to %s], actual updated: %s\n" , m . updatedRange [ 0 ] , m . updatedRange [ 1 ] , time . UnixMilli ( obj . UpdatedAt ) )
2022-10-04 11:57:26 -07:00
}
2022-10-05 18:46:17 -07:00
if m . updatedBy != "" && m . updatedBy != obj . UpdatedBy {
mismatches += fmt . Sprintf ( "updatedBy: expected:%s, found:%s\n" , m . updatedBy , obj . UpdatedBy )
2022-10-04 11:57:26 -07:00
}
if m . version != nil && * m . version != obj . Version {
mismatches += fmt . Sprintf ( "expected version: %s, actual version: %s\n" , * m . version , obj . Version )
2022-09-30 23:56:07 +04:00
}
require . True ( t , len ( mismatches ) == 0 , mismatches )
}
2022-11-30 13:42:42 -08:00
func TestIntegrationEntityServer ( t * testing . T ) {
2022-09-30 23:56:07 +04:00
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
testCtx := createTestContext ( t )
2022-10-31 07:26:16 -07:00
ctx := metadata . AppendToOutgoingContext ( testCtx . ctx , "authorization" , fmt . Sprintf ( "Bearer %s" , testCtx . authToken ) )
2022-09-30 23:56:07 +04:00
2022-11-04 09:37:25 -07:00
fakeUser := store . GetUserIDString ( testCtx . user )
2022-09-30 23:56:07 +04:00
firstVersion := "1"
2022-11-01 08:28:13 -07:00
kind := models . StandardKindJSONObj
2022-11-30 13:42:42 -08:00
grn := & entity . GRN {
2022-11-30 12:10:35 -08:00
Kind : kind ,
UID : "my-test-entity" ,
2022-10-31 07:26:16 -07:00
}
2022-09-30 23:56:07 +04:00
body := [ ] byte ( "{\"name\":\"John\"}" )
t . Run ( "should not retrieve non-existent objects" , func ( t * testing . T ) {
2022-11-30 13:42:42 -08:00
resp , err := testCtx . client . Read ( ctx , & entity . ReadEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-09-30 23:56:07 +04:00
} )
require . NoError ( t , err )
require . NotNil ( t , resp )
2022-11-30 13:42:42 -08:00
require . Nil ( t , resp . Entity )
2022-09-30 23:56:07 +04:00
} )
t . Run ( "should be able to read persisted objects" , func ( t * testing . T ) {
before := time . Now ( )
2022-11-30 13:42:42 -08:00
writeReq := & entity . WriteEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-09-30 23:56:07 +04:00
Body : body ,
Comment : "first entity!" ,
}
writeResp , err := testCtx . client . Write ( ctx , writeReq )
require . NoError ( t , err )
2022-10-04 11:57:26 -07:00
versionMatcher := objectVersionMatcher {
2022-10-05 11:58:46 -07:00
updatedRange : [ ] time . Time { before , time . Now ( ) } ,
updatedBy : fakeUser ,
version : & firstVersion ,
comment : & writeReq . Comment ,
2022-09-30 23:56:07 +04:00
}
2022-11-30 13:42:42 -08:00
requireVersionMatch ( t , writeResp . Entity , versionMatcher )
2022-09-30 23:56:07 +04:00
2022-11-30 13:42:42 -08:00
readResp , err := testCtx . client . Read ( ctx , & entity . ReadEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-09-30 23:56:07 +04:00
Version : "" ,
WithBody : true ,
} )
require . NoError ( t , err )
require . Nil ( t , readResp . SummaryJson )
2022-11-30 13:42:42 -08:00
require . NotNil ( t , readResp . Entity )
2022-10-04 11:57:26 -07:00
2022-11-30 13:42:42 -08:00
foundGRN := readResp . Entity . GRN
2022-10-31 07:26:16 -07:00
require . NotNil ( t , foundGRN )
2022-11-01 17:33:36 -07:00
require . Equal ( t , testCtx . user . OrgID , foundGRN . TenantId ) // orgId becomes the tenant id when not set
2022-10-31 07:26:16 -07:00
require . Equal ( t , grn . Kind , foundGRN . Kind )
require . Equal ( t , grn . UID , foundGRN . UID )
2022-11-30 13:42:42 -08:00
objectMatcher := rawEntityMatcher {
2022-10-31 07:26:16 -07:00
grn : grn ,
2022-10-05 11:58:46 -07:00
createdRange : [ ] time . Time { before , time . Now ( ) } ,
updatedRange : [ ] time . Time { before , time . Now ( ) } ,
createdBy : fakeUser ,
updatedBy : fakeUser ,
body : body ,
version : & firstVersion ,
2022-10-04 11:57:26 -07:00
}
2022-11-30 13:42:42 -08:00
requireEntityMatch ( t , readResp . Entity , objectMatcher )
2022-09-30 23:56:07 +04:00
2022-11-30 13:42:42 -08:00
deleteResp , err := testCtx . client . Delete ( ctx , & entity . DeleteEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-11-30 13:42:42 -08:00
PreviousVersion : writeResp . Entity . Version ,
2022-09-30 23:56:07 +04:00
} )
require . NoError ( t , err )
require . True ( t , deleteResp . OK )
2022-11-30 13:42:42 -08:00
readRespAfterDelete , err := testCtx . client . Read ( ctx , & entity . ReadEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-09-30 23:56:07 +04:00
Version : "" ,
WithBody : true ,
} )
require . NoError ( t , err )
2022-11-30 13:42:42 -08:00
require . Nil ( t , readRespAfterDelete . Entity )
2022-09-30 23:56:07 +04:00
} )
t . Run ( "should be able to update an object" , func ( t * testing . T ) {
before := time . Now ( )
2022-11-30 13:42:42 -08:00
grn := & entity . GRN {
2022-11-30 12:10:35 -08:00
Kind : kind ,
UID : util . GenerateShortUID ( ) ,
2022-11-01 17:33:36 -07:00
}
2022-11-30 13:42:42 -08:00
writeReq1 := & entity . WriteEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-09-30 23:56:07 +04:00
Body : body ,
Comment : "first entity!" ,
}
writeResp1 , err := testCtx . client . Write ( ctx , writeReq1 )
require . NoError ( t , err )
2022-11-30 13:42:42 -08:00
require . Equal ( t , entity . WriteEntityResponse_CREATED , writeResp1 . Status )
2022-09-30 23:56:07 +04:00
body2 := [ ] byte ( "{\"name\":\"John2\"}" )
2022-11-30 13:42:42 -08:00
writeReq2 := & entity . WriteEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-09-30 23:56:07 +04:00
Body : body2 ,
Comment : "update1" ,
}
writeResp2 , err := testCtx . client . Write ( ctx , writeReq2 )
require . NoError ( t , err )
2022-11-30 13:42:42 -08:00
require . NotEqual ( t , writeResp1 . Entity . Version , writeResp2 . Entity . Version )
2022-09-30 23:56:07 +04:00
2022-10-04 11:57:26 -07:00
// Duplicate write (no change)
writeDupRsp , err := testCtx . client . Write ( ctx , writeReq2 )
require . NoError ( t , err )
require . Nil ( t , writeDupRsp . Error )
2022-11-30 13:42:42 -08:00
require . Equal ( t , entity . WriteEntityResponse_UNCHANGED , writeDupRsp . Status )
require . Equal ( t , writeResp2 . Entity . Version , writeDupRsp . Entity . Version )
require . Equal ( t , writeResp2 . Entity . ETag , writeDupRsp . Entity . ETag )
2022-10-04 11:57:26 -07:00
2022-09-30 23:56:07 +04:00
body3 := [ ] byte ( "{\"name\":\"John3\"}" )
2022-11-30 13:42:42 -08:00
writeReq3 := & entity . WriteEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-09-30 23:56:07 +04:00
Body : body3 ,
Comment : "update3" ,
}
writeResp3 , err := testCtx . client . Write ( ctx , writeReq3 )
require . NoError ( t , err )
2022-11-30 13:42:42 -08:00
require . NotEqual ( t , writeResp3 . Entity . Version , writeResp2 . Entity . Version )
2022-09-30 23:56:07 +04:00
2022-11-30 13:42:42 -08:00
latestMatcher := rawEntityMatcher {
2022-10-31 07:26:16 -07:00
grn : grn ,
2022-10-05 11:58:46 -07:00
createdRange : [ ] time . Time { before , time . Now ( ) } ,
updatedRange : [ ] time . Time { before , time . Now ( ) } ,
createdBy : fakeUser ,
updatedBy : fakeUser ,
body : body3 ,
2022-11-30 13:42:42 -08:00
version : & writeResp3 . Entity . Version ,
2022-09-30 23:56:07 +04:00
}
2022-11-30 13:42:42 -08:00
readRespLatest , err := testCtx . client . Read ( ctx , & entity . ReadEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-09-30 23:56:07 +04:00
Version : "" , // latest
WithBody : true ,
} )
require . NoError ( t , err )
require . Nil ( t , readRespLatest . SummaryJson )
2022-11-30 13:42:42 -08:00
requireEntityMatch ( t , readRespLatest . Entity , latestMatcher )
2022-09-30 23:56:07 +04:00
2022-11-30 13:42:42 -08:00
readRespFirstVer , err := testCtx . client . Read ( ctx , & entity . ReadEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-11-30 13:42:42 -08:00
Version : writeResp1 . Entity . Version ,
2022-09-30 23:56:07 +04:00
WithBody : true ,
} )
require . NoError ( t , err )
require . Nil ( t , readRespFirstVer . SummaryJson )
2022-11-30 13:42:42 -08:00
require . NotNil ( t , readRespFirstVer . Entity )
requireEntityMatch ( t , readRespFirstVer . Entity , rawEntityMatcher {
2022-10-31 07:26:16 -07:00
grn : grn ,
2022-10-05 11:58:46 -07:00
createdRange : [ ] time . Time { before , time . Now ( ) } ,
updatedRange : [ ] time . Time { before , time . Now ( ) } ,
createdBy : fakeUser ,
updatedBy : fakeUser ,
body : body ,
version : & firstVersion ,
2022-09-30 23:56:07 +04:00
} )
2022-11-30 13:42:42 -08:00
history , err := testCtx . client . History ( ctx , & entity . EntityHistoryRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-09-30 23:56:07 +04:00
} )
require . NoError ( t , err )
2022-11-30 13:42:42 -08:00
require . Equal ( t , [ ] * entity . EntityVersionInfo {
writeResp3 . Entity ,
writeResp2 . Entity ,
writeResp1 . Entity ,
2022-10-04 11:57:26 -07:00
} , history . Versions )
2022-09-30 23:56:07 +04:00
2022-11-30 13:42:42 -08:00
deleteResp , err := testCtx . client . Delete ( ctx , & entity . DeleteEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-11-30 13:42:42 -08:00
PreviousVersion : writeResp3 . Entity . Version ,
2022-09-30 23:56:07 +04:00
} )
require . NoError ( t , err )
require . True ( t , deleteResp . OK )
} )
t . Run ( "should be able to search for objects" , func ( t * testing . T ) {
uid2 := "uid2"
uid3 := "uid3"
uid4 := "uid4"
2022-11-01 08:28:13 -07:00
kind2 := models . StandardKindPlaylist
2022-11-30 13:42:42 -08:00
w1 , err := testCtx . client . Write ( ctx , & entity . WriteEntityRequest {
2022-10-31 07:26:16 -07:00
GRN : grn ,
2022-09-30 23:56:07 +04:00
Body : body ,
} )
require . NoError ( t , err )
2022-11-30 13:42:42 -08:00
w2 , err := testCtx . client . Write ( ctx , & entity . WriteEntityRequest {
GRN : & entity . GRN {
2022-11-30 12:10:35 -08:00
UID : uid2 ,
Kind : kind ,
2022-10-31 07:26:16 -07:00
} ,
2022-09-30 23:56:07 +04:00
Body : body ,
} )
require . NoError ( t , err )
2022-11-30 13:42:42 -08:00
w3 , err := testCtx . client . Write ( ctx , & entity . WriteEntityRequest {
GRN : & entity . GRN {
2022-11-30 12:10:35 -08:00
UID : uid3 ,
Kind : kind2 ,
2022-10-31 07:26:16 -07:00
} ,
2022-09-30 23:56:07 +04:00
Body : body ,
} )
require . NoError ( t , err )
2022-11-30 13:42:42 -08:00
w4 , err := testCtx . client . Write ( ctx , & entity . WriteEntityRequest {
GRN : & entity . GRN {
2022-11-30 12:10:35 -08:00
UID : uid4 ,
Kind : kind2 ,
2022-10-31 07:26:16 -07:00
} ,
2022-09-30 23:56:07 +04:00
Body : body ,
} )
require . NoError ( t , err )
2022-11-30 13:42:42 -08:00
search , err := testCtx . client . Search ( ctx , & entity . EntitySearchRequest {
2022-10-04 11:57:26 -07:00
Kind : [ ] string { kind , kind2 } ,
WithBody : false ,
2022-09-30 23:56:07 +04:00
} )
require . NoError ( t , err )
2022-10-04 11:57:26 -07:00
require . NotNil ( t , search )
uids := make ( [ ] string , 0 , len ( search . Results ) )
kinds := make ( [ ] string , 0 , len ( search . Results ) )
version := make ( [ ] string , 0 , len ( search . Results ) )
for _ , res := range search . Results {
2022-10-31 07:26:16 -07:00
uids = append ( uids , res . GRN . UID )
kinds = append ( kinds , res . GRN . Kind )
2022-10-04 11:57:26 -07:00
version = append ( version , res . Version )
}
require . Equal ( t , [ ] string { "my-test-entity" , "uid2" , "uid3" , "uid4" } , uids )
2022-11-01 08:28:13 -07:00
require . Equal ( t , [ ] string { "jsonobj" , "jsonobj" , "playlist" , "playlist" } , kinds )
2022-10-04 11:57:26 -07:00
require . Equal ( t , [ ] string {
2022-11-30 13:42:42 -08:00
w1 . Entity . Version ,
w2 . Entity . Version ,
w3 . Entity . Version ,
w4 . Entity . Version ,
2022-10-04 11:57:26 -07:00
} , version )
// Again with only one kind
2022-11-30 13:42:42 -08:00
searchKind1 , err := testCtx . client . Search ( ctx , & entity . EntitySearchRequest {
2022-09-30 23:56:07 +04:00
Kind : [ ] string { kind } ,
} )
require . NoError ( t , err )
2022-10-04 11:57:26 -07:00
uids = make ( [ ] string , 0 , len ( searchKind1 . Results ) )
kinds = make ( [ ] string , 0 , len ( searchKind1 . Results ) )
version = make ( [ ] string , 0 , len ( searchKind1 . Results ) )
for _ , res := range searchKind1 . Results {
2022-10-31 07:26:16 -07:00
uids = append ( uids , res . GRN . UID )
kinds = append ( kinds , res . GRN . Kind )
2022-10-04 11:57:26 -07:00
version = append ( version , res . Version )
}
require . Equal ( t , [ ] string { "my-test-entity" , "uid2" } , uids )
2022-11-01 08:28:13 -07:00
require . Equal ( t , [ ] string { "jsonobj" , "jsonobj" } , kinds )
2022-10-04 11:57:26 -07:00
require . Equal ( t , [ ] string {
2022-11-30 13:42:42 -08:00
w1 . Entity . Version ,
w2 . Entity . Version ,
2022-10-04 11:57:26 -07:00
} , version )
2022-09-30 23:56:07 +04:00
} )
}