@@ -4,8 +4,10 @@ import (
44 "testing"
55
66 "github.com/stretchr/testify/require"
7+ "google.golang.org/protobuf/types/known/anypb"
78
89 corev1 "github.com/authzed/spicedb/pkg/proto/core/v1"
10+ implv1 "github.com/authzed/spicedb/pkg/proto/impl/v1"
911)
1012
1113func TestConvertDefinitionEdgeCases (t * testing.T ) {
@@ -831,3 +833,271 @@ func TestConvertFunctionType(t *testing.T) {
831833 })
832834 }
833835}
836+
837+ func TestMetadataPreservation (t * testing.T ) {
838+ t .Parallel ()
839+
840+ t .Run ("namespace definition metadata with doc comments is preserved" , func (t * testing.T ) {
841+ t .Parallel ()
842+
843+ // Create doc comment metadata
844+ docComment := & implv1.DocComment {
845+ Comment : "This is a test resource" ,
846+ }
847+ docCommentAny , err := anypb .New (docComment )
848+ require .NoError (t , err )
849+
850+ originalMetadata := & corev1.Metadata {
851+ MetadataMessage : []* anypb.Any {docCommentAny },
852+ }
853+
854+ originalDef := & corev1.NamespaceDefinition {
855+ Name : "test_resource" ,
856+ Relation : []* corev1.Relation {
857+ {
858+ Name : "viewer" ,
859+ TypeInformation : & corev1.TypeInformation {
860+ AllowedDirectRelations : []* corev1.AllowedRelation {
861+ {
862+ Namespace : "user" ,
863+ RelationOrWildcard : & corev1.AllowedRelation_Relation {
864+ Relation : "" ,
865+ },
866+ },
867+ },
868+ },
869+ },
870+ },
871+ Metadata : originalMetadata ,
872+ }
873+
874+ // Convert to v2 schema
875+ schema , err := BuildSchemaFromDefinitions ([]* corev1.NamespaceDefinition {originalDef }, nil )
876+ require .NoError (t , err )
877+
878+ // Verify internal representation
879+ def , ok := schema .Definitions ()["test_resource" ]
880+ require .True (t , ok )
881+ require .NotNil (t , def .metadata )
882+ require .Equal (t , []string {"This is a test resource" }, def .metadata .Comments ())
883+
884+ // Convert back to corev1
885+ defs , caveats , err := schema .ToDefinitions ()
886+ require .NoError (t , err )
887+ require .Len (t , defs , 1 )
888+ require .Len (t , caveats , 0 )
889+
890+ // Verify metadata is preserved
891+ require .NotNil (t , defs [0 ].Metadata , "Metadata should be preserved during round-trip conversion" )
892+ require .Len (t , defs [0 ].Metadata .MetadataMessage , 1 )
893+
894+ // Verify doc comment
895+ var roundTrippedComment implv1.DocComment
896+ err = defs [0 ].Metadata .MetadataMessage [0 ].UnmarshalTo (& roundTrippedComment )
897+ require .NoError (t , err )
898+ require .Equal (t , "This is a test resource" , roundTrippedComment .Comment )
899+ })
900+
901+ t .Run ("caveat definition metadata with doc comments is preserved" , func (t * testing.T ) {
902+ t .Parallel ()
903+
904+ // Create doc comment metadata
905+ docComment := & implv1.DocComment {
906+ Comment : "This is a test caveat" ,
907+ }
908+ docCommentAny , err := anypb .New (docComment )
909+ require .NoError (t , err )
910+
911+ originalMetadata := & corev1.Metadata {
912+ MetadataMessage : []* anypb.Any {docCommentAny },
913+ }
914+
915+ originalCaveat := & corev1.CaveatDefinition {
916+ Name : "test_caveat" ,
917+ SerializedExpression : []byte ("x == 1" ),
918+ ParameterTypes : map [string ]* corev1.CaveatTypeReference {
919+ "x" : {
920+ TypeName : "int" ,
921+ },
922+ },
923+ Metadata : originalMetadata ,
924+ }
925+
926+ // Convert to v2 schema
927+ schema , err := BuildSchemaFromDefinitions (nil , []* corev1.CaveatDefinition {originalCaveat })
928+ require .NoError (t , err )
929+
930+ // Verify internal representation
931+ caveat , ok := schema .Caveats ()["test_caveat" ]
932+ require .True (t , ok )
933+ require .NotNil (t , caveat .metadata )
934+ require .Equal (t , []string {"This is a test caveat" }, caveat .metadata .Comments ())
935+
936+ // Convert back to corev1
937+ defs , caveats , err := schema .ToDefinitions ()
938+ require .NoError (t , err )
939+ require .Len (t , defs , 0 )
940+ require .Len (t , caveats , 1 )
941+
942+ // Verify metadata is preserved
943+ require .NotNil (t , caveats [0 ].Metadata , "Caveat metadata should be preserved during round-trip conversion" )
944+
945+ // Verify doc comment
946+ var roundTrippedComment implv1.DocComment
947+ err = caveats [0 ].Metadata .MetadataMessage [0 ].UnmarshalTo (& roundTrippedComment )
948+ require .NoError (t , err )
949+ require .Equal (t , "This is a test caveat" , roundTrippedComment .Comment )
950+ })
951+
952+ t .Run ("relation metadata with doc comments and relation kind is preserved" , func (t * testing.T ) {
953+ t .Parallel ()
954+
955+ // Create doc comment metadata
956+ docComment := & implv1.DocComment {
957+ Comment : "This is a viewer relation" ,
958+ }
959+ docCommentAny , err := anypb .New (docComment )
960+ require .NoError (t , err )
961+
962+ // Create relation metadata
963+ relationMetadata := & implv1.RelationMetadata {
964+ Kind : implv1 .RelationMetadata_RELATION ,
965+ }
966+ relationMetadataAny , err := anypb .New (relationMetadata )
967+ require .NoError (t , err )
968+
969+ combinedMetadata := & corev1.Metadata {
970+ MetadataMessage : []* anypb.Any {docCommentAny , relationMetadataAny },
971+ }
972+
973+ originalDef := & corev1.NamespaceDefinition {
974+ Name : "test_resource" ,
975+ Relation : []* corev1.Relation {
976+ {
977+ Name : "viewer" ,
978+ TypeInformation : & corev1.TypeInformation {
979+ AllowedDirectRelations : []* corev1.AllowedRelation {
980+ {
981+ Namespace : "user" ,
982+ RelationOrWildcard : & corev1.AllowedRelation_Relation {
983+ Relation : "" ,
984+ },
985+ },
986+ },
987+ },
988+ Metadata : combinedMetadata ,
989+ },
990+ },
991+ }
992+
993+ // Convert to v2 schema
994+ schema , err := BuildSchemaFromDefinitions ([]* corev1.NamespaceDefinition {originalDef }, nil )
995+ require .NoError (t , err )
996+
997+ // Verify internal representation
998+ def , ok := schema .Definitions ()["test_resource" ]
999+ require .True (t , ok )
1000+ rel , ok := def .Relations ()["viewer" ]
1001+ require .True (t , ok )
1002+ require .NotNil (t , rel .metadata )
1003+ require .Equal (t , []string {"This is a viewer relation" }, rel .metadata .Comments ())
1004+ require .Equal (t , RelationKindRelation , rel .metadata .RelationKind ())
1005+
1006+ // Convert back to corev1
1007+ defs , _ , err := schema .ToDefinitions ()
1008+ require .NoError (t , err )
1009+ require .Len (t , defs , 1 )
1010+ require .Len (t , defs [0 ].Relation , 1 )
1011+
1012+ // Verify relation metadata is preserved
1013+ require .NotNil (t , defs [0 ].Relation [0 ].Metadata , "Relation metadata should be preserved during round-trip conversion" )
1014+ require .Len (t , defs [0 ].Relation [0 ].Metadata .MetadataMessage , 2 )
1015+
1016+ // Verify both doc comment and relation metadata are preserved
1017+ var foundDocComment , foundRelationMetadata bool
1018+ for _ , msg := range defs [0 ].Relation [0 ].Metadata .MetadataMessage {
1019+ var dc implv1.DocComment
1020+ if err := msg .UnmarshalTo (& dc ); err == nil {
1021+ require .Equal (t , "This is a viewer relation" , dc .Comment )
1022+ foundDocComment = true
1023+ continue
1024+ }
1025+
1026+ var rm implv1.RelationMetadata
1027+ if err := msg .UnmarshalTo (& rm ); err == nil {
1028+ require .Equal (t , implv1 .RelationMetadata_RELATION , rm .Kind )
1029+ foundRelationMetadata = true
1030+ }
1031+ }
1032+
1033+ require .True (t , foundDocComment , "Doc comment should be preserved" )
1034+ require .True (t , foundRelationMetadata , "Relation metadata should be preserved" )
1035+ })
1036+
1037+ t .Run ("permission metadata with type annotations is preserved" , func (t * testing.T ) {
1038+ t .Parallel ()
1039+
1040+ // Create permission metadata with type annotations
1041+ permissionMetadata := & implv1.RelationMetadata {
1042+ Kind : implv1 .RelationMetadata_PERMISSION ,
1043+ TypeAnnotations : & implv1.TypeAnnotations {
1044+ Types : []string {"user" , "group" },
1045+ },
1046+ }
1047+ permissionMetadataAny , err := anypb .New (permissionMetadata )
1048+ require .NoError (t , err )
1049+
1050+ metadata := & corev1.Metadata {
1051+ MetadataMessage : []* anypb.Any {permissionMetadataAny },
1052+ }
1053+
1054+ originalDef := & corev1.NamespaceDefinition {
1055+ Name : "test_resource" ,
1056+ Relation : []* corev1.Relation {
1057+ {
1058+ Name : "viewer" ,
1059+ TypeInformation : & corev1.TypeInformation {
1060+ AllowedDirectRelations : []* corev1.AllowedRelation {
1061+ {
1062+ Namespace : "user" ,
1063+ RelationOrWildcard : & corev1.AllowedRelation_Relation {
1064+ Relation : "" ,
1065+ },
1066+ },
1067+ },
1068+ },
1069+ Metadata : metadata ,
1070+ },
1071+ },
1072+ }
1073+
1074+ // Convert to v2 schema
1075+ schema , err := BuildSchemaFromDefinitions ([]* corev1.NamespaceDefinition {originalDef }, nil )
1076+ require .NoError (t , err )
1077+
1078+ // Verify internal representation
1079+ def , ok := schema .Definitions ()["test_resource" ]
1080+ require .True (t , ok )
1081+ rel , ok := def .Relations ()["viewer" ]
1082+ require .True (t , ok )
1083+ require .NotNil (t , rel .metadata )
1084+ require .Equal (t , RelationKindPermission , rel .metadata .RelationKind ())
1085+ require .Equal (t , []string {"user" , "group" }, rel .metadata .TypeAnnotations ())
1086+
1087+ // Convert back to corev1
1088+ defs , _ , err := schema .ToDefinitions ()
1089+ require .NoError (t , err )
1090+ require .Len (t , defs , 1 )
1091+ require .Len (t , defs [0 ].Relation , 1 )
1092+
1093+ // Verify permission metadata with type annotations is preserved
1094+ require .NotNil (t , defs [0 ].Relation [0 ].Metadata )
1095+
1096+ var rm implv1.RelationMetadata
1097+ err = defs [0 ].Relation [0 ].Metadata .MetadataMessage [0 ].UnmarshalTo (& rm )
1098+ require .NoError (t , err )
1099+ require .Equal (t , implv1 .RelationMetadata_PERMISSION , rm .Kind )
1100+ require .NotNil (t , rm .TypeAnnotations )
1101+ require .Equal (t , []string {"user" , "group" }, rm .TypeAnnotations .Types )
1102+ })
1103+ }
0 commit comments