@@ -22,6 +22,8 @@ import (
2222 "fmt"
2323 "testing"
2424
25+ "github.com/chenmingyong0423/go-mongox/builder/update"
26+
2527 "github.com/chenmingyong0423/go-mongox/internal/pkg/utils"
2628
2729 "github.com/chenmingyong0423/go-mongox/callback"
@@ -1091,3 +1093,281 @@ func TestFinder_e2e_DistinctWithParse(t *testing.T) {
10911093 require .Error (t , err )
10921094 })
10931095}
1096+
1097+ func TestFinder_e2e_FindOneAndUpdate (t * testing.T ) {
1098+ collection := getCollection (t )
1099+ finder := NewFinder [TestUser ](collection )
1100+
1101+ type globalHook struct {
1102+ opType operation.OpType
1103+ name string
1104+ fn callback.CbFn
1105+ }
1106+ testCases := []struct {
1107+ name string
1108+ before func (ctx context.Context , t * testing.T )
1109+ after func (ctx context.Context , t * testing.T )
1110+
1111+ filter any
1112+ updates any
1113+ opts []* options.FindOneAndUpdateOptions
1114+ globalHook []globalHook
1115+ beforeHook []beforeHookFn
1116+ afterHook []afterHookFn [TestUser ]
1117+
1118+ ctx context.Context
1119+ want * TestUser
1120+ wantErr error
1121+ }{
1122+ {
1123+ name : "nil document" ,
1124+ before : func (ctx context.Context , t * testing.T ) {
1125+ insertOneResult , err := collection .InsertOne (ctx , & TestUser {
1126+ Name : "chenmingyong" ,
1127+ Age : 24 ,
1128+ })
1129+ require .NoError (t , err )
1130+ require .NotNil (t , insertOneResult .InsertedID )
1131+ },
1132+ after : func (ctx context.Context , t * testing.T ) {
1133+ deleteOneResult , err := collection .DeleteOne (ctx , query .Eq ("name" , "chenmingyong" ))
1134+ require .NoError (t , err )
1135+ require .Equal (t , int64 (1 ), deleteOneResult .DeletedCount )
1136+
1137+ finder .filter = bson.D {}
1138+ },
1139+ filter : query .Eq ("name" , "burt" ),
1140+ wantErr : mongo .ErrNilDocument ,
1141+ },
1142+ {
1143+ name : "find by name and update age" ,
1144+ before : func (ctx context.Context , t * testing.T ) {
1145+ insertOneResult , err := collection .InsertOne (ctx , & TestUser {
1146+ Name : "chenmingyong" ,
1147+ Age : 18 ,
1148+ })
1149+ require .NoError (t , err )
1150+ require .NotNil (t , insertOneResult .InsertedID )
1151+ },
1152+ after : func (ctx context.Context , t * testing.T ) {
1153+ deleteOneResult , err := collection .DeleteOne (ctx , query .Eq ("name" , "chenmingyong" ))
1154+ require .NoError (t , err )
1155+ require .Equal (t , int64 (1 ), deleteOneResult .DeletedCount )
1156+
1157+ finder .filter = bson.D {}
1158+ },
1159+ filter : query .Eq ("name" , "chenmingyong" ),
1160+ updates : update .Set ("age" , 24 ),
1161+ opts : []* options.FindOneAndUpdateOptions {options .FindOneAndUpdate ().SetReturnDocument (options .After )},
1162+ want : & TestUser {
1163+ Name : "chenmingyong" ,
1164+ Age : 24 ,
1165+ },
1166+ },
1167+ {
1168+ name : "global before hook error" ,
1169+ before : func (ctx context.Context , t * testing.T ) {},
1170+ after : func (ctx context.Context , t * testing.T ) {},
1171+ filter : query .Eq ("name" , "Mingyong Chen" ),
1172+ globalHook : []globalHook {
1173+ {
1174+ opType : operation .OpTypeBeforeFind ,
1175+ name : "before hook error" ,
1176+ fn : func (ctx context.Context , opCtx * operation.OpContext , opts ... any ) error {
1177+ return errors .New ("global before hook error" )
1178+ },
1179+ },
1180+ },
1181+ wantErr : errors .New ("global before hook error" ),
1182+ },
1183+ {
1184+ name : "global after hook error" ,
1185+ before : func (ctx context.Context , t * testing.T ) {
1186+ insertOneResult , err := collection .InsertOne (ctx , & TestUser {
1187+ Name : "chenmingyong" ,
1188+ Age : 18 ,
1189+ })
1190+ require .NoError (t , err )
1191+ require .NotNil (t , insertOneResult .InsertedID )
1192+ },
1193+ after : func (ctx context.Context , t * testing.T ) {
1194+ deleteOneResult , err := collection .DeleteOne (ctx , query .Eq ("name" , "chenmingyong" ))
1195+ require .NoError (t , err )
1196+ require .Equal (t , int64 (1 ), deleteOneResult .DeletedCount )
1197+
1198+ finder .filter = bson.D {}
1199+ },
1200+ filter : query .Eq ("name" , "chenmingyong" ),
1201+ updates : update .Set ("age" , 24 ),
1202+ opts : []* options.FindOneAndUpdateOptions {options .FindOneAndUpdate ().SetReturnDocument (options .After )},
1203+ globalHook : []globalHook {
1204+ {
1205+ opType : operation .OpTypeAfterFind ,
1206+ name : "after hook error" ,
1207+ fn : func (ctx context.Context , opCtx * operation.OpContext , opts ... any ) error {
1208+ return errors .New ("global after hook error" )
1209+ },
1210+ },
1211+ },
1212+ wantErr : errors .New ("global after hook error" ),
1213+ },
1214+ {
1215+ name : "global before and after hook" ,
1216+ before : func (ctx context.Context , t * testing.T ) {
1217+ insertOneResult , err := collection .InsertOne (ctx , & TestUser {
1218+ Name : "chenmingyong" ,
1219+ Age : 18 ,
1220+ })
1221+ require .NoError (t , err )
1222+ require .NotNil (t , insertOneResult .InsertedID )
1223+ },
1224+ after : func (ctx context.Context , t * testing.T ) {
1225+ deleteOneResult , err := collection .DeleteOne (ctx , query .Eq ("name" , "chenmingyong" ))
1226+ require .NoError (t , err )
1227+ require .Equal (t , int64 (1 ), deleteOneResult .DeletedCount )
1228+
1229+ finder .filter = bson.D {}
1230+ },
1231+ filter : query .Eq ("name" , "chenmingyong" ),
1232+ updates : update .Set ("age" , 24 ),
1233+ opts : []* options.FindOneAndUpdateOptions {options .FindOneAndUpdate ().SetReturnDocument (options .After )},
1234+ globalHook : []globalHook {
1235+ {
1236+ opType : operation .OpTypeBeforeFind ,
1237+ name : "before hook" ,
1238+ fn : func (ctx context.Context , opCtx * operation.OpContext , opts ... any ) error {
1239+ if opCtx .Filter .(bson.D )[0 ].Key != "name" || opCtx .Filter .(bson.D )[0 ].Value .(bson.D )[0 ].Value != "chenmingyong" {
1240+ return errors .New ("filter error" )
1241+ }
1242+ if opCtx .Updates .(bson.D )[0 ].Value .(bson.D )[0 ].Key != "age" || opCtx .Updates .(bson.D )[0 ].Value .(bson.D )[0 ].Value != 24 {
1243+ return errors .New ("updates error" )
1244+ }
1245+ return nil
1246+ },
1247+ },
1248+ {
1249+ opType : operation .OpTypeAfterFind ,
1250+ name : "after hook" ,
1251+ fn : func (ctx context.Context , opCtx * operation.OpContext , opts ... any ) error {
1252+ user := opCtx .Doc .(* TestUser )
1253+ if user .Name != "chenmingyong" || user .Age != 24 {
1254+ return errors .New ("result error" )
1255+ }
1256+ return nil
1257+ },
1258+ },
1259+ },
1260+ want : & TestUser {
1261+ Name : "chenmingyong" ,
1262+ Age : 24 ,
1263+ },
1264+ },
1265+ {
1266+ name : "before hook error" ,
1267+ before : func (ctx context.Context , t * testing.T ) {},
1268+ after : func (ctx context.Context , t * testing.T ) {},
1269+ filter : query .Eq ("name" , "chenmingyong" ),
1270+ beforeHook : []beforeHookFn {
1271+ func (ctx context.Context , opCtx * OpContext , opts ... any ) error {
1272+ return errors .New ("before hook error" )
1273+ },
1274+ },
1275+ wantErr : errors .New ("before hook error" ),
1276+ },
1277+ {
1278+ name : "after hook error" ,
1279+ before : func (ctx context.Context , t * testing.T ) {
1280+ insertOneResult , err := collection .InsertOne (ctx , & TestUser {
1281+ Name : "chenmingyong" ,
1282+ Age : 18 ,
1283+ })
1284+ require .NoError (t , err )
1285+ require .NotNil (t , insertOneResult .InsertedID )
1286+ },
1287+ after : func (ctx context.Context , t * testing.T ) {
1288+ deleteOneResult , err := collection .DeleteOne (ctx , query .Eq ("name" , "chenmingyong" ))
1289+ require .NoError (t , err )
1290+ require .Equal (t , int64 (1 ), deleteOneResult .DeletedCount )
1291+
1292+ finder .filter = bson.D {}
1293+ },
1294+ filter : query .Eq ("name" , "chenmingyong" ),
1295+ updates : update .Set ("age" , 24 ),
1296+ opts : []* options.FindOneAndUpdateOptions {options .FindOneAndUpdate ().SetReturnDocument (options .After )},
1297+ afterHook : []afterHookFn [TestUser ]{
1298+ func (ctx context.Context , opCtx * AfterOpContext [TestUser ], opts ... any ) error {
1299+ return errors .New ("after hook error" )
1300+ },
1301+ },
1302+ wantErr : errors .New ("after hook error" ),
1303+ },
1304+ {
1305+ name : "before and after hook" ,
1306+ before : func (ctx context.Context , t * testing.T ) {
1307+ insertOneResult , err := collection .InsertOne (ctx , & TestUser {
1308+ Name : "chenmingyong" ,
1309+ Age : 18 ,
1310+ })
1311+ require .NoError (t , err )
1312+ require .NotNil (t , insertOneResult .InsertedID )
1313+ },
1314+ after : func (ctx context.Context , t * testing.T ) {
1315+ deleteOneResult , err := collection .DeleteOne (ctx , query .Eq ("name" , "chenmingyong" ))
1316+ require .NoError (t , err )
1317+ require .Equal (t , int64 (1 ), deleteOneResult .DeletedCount )
1318+
1319+ finder .filter = bson.D {}
1320+ },
1321+ filter : query .Eq ("name" , "chenmingyong" ),
1322+ updates : update .Set ("age" , 24 ),
1323+ opts : []* options.FindOneAndUpdateOptions {options .FindOneAndUpdate ().SetReturnDocument (options .After )},
1324+ beforeHook : []beforeHookFn {
1325+ func (ctx context.Context , opCtx * OpContext , opts ... any ) error {
1326+ if opCtx .Filter .(bson.D )[0 ].Key != "name" || opCtx .Filter .(bson.D )[0 ].Value .(bson.D )[0 ].Value != "chenmingyong" {
1327+ return errors .New ("filter error" )
1328+ }
1329+ if opCtx .Updates .(bson.D )[0 ].Value .(bson.D )[0 ].Key != "age" || opCtx .Updates .(bson.D )[0 ].Value .(bson.D )[0 ].Value != 24 {
1330+ return errors .New ("updates error" )
1331+ }
1332+ return nil
1333+ },
1334+ },
1335+ afterHook : []afterHookFn [TestUser ]{
1336+ func (ctx context.Context , opCtx * AfterOpContext [TestUser ], opts ... any ) error {
1337+ user := opCtx .Doc
1338+ if user .Name != "chenmingyong" || user .Age != 24 {
1339+ return errors .New ("after error" )
1340+ }
1341+ return nil
1342+ },
1343+ },
1344+ want : & TestUser {
1345+ Name : "chenmingyong" ,
1346+ Age : 24 ,
1347+ },
1348+ },
1349+ }
1350+
1351+ for _ , tc := range testCases {
1352+ t .Run (tc .name , func (t * testing.T ) {
1353+ tc .before (tc .ctx , t )
1354+ for _ , hook := range tc .globalHook {
1355+ callback .GetCallback ().Register (hook .opType , hook .name , hook .fn )
1356+ }
1357+ user , err := finder .RegisterBeforeHooks (tc .beforeHook ... ).
1358+ RegisterAfterHooks (tc .afterHook ... ).Filter (tc .filter ).Updates (tc .updates ).
1359+ FindOneAndUpdate (tc .ctx , tc .opts ... )
1360+ tc .after (tc .ctx , t )
1361+ require .Equal (t , tc .wantErr , err )
1362+ if err == nil {
1363+ tc .want .ID = user .ID
1364+ require .Equal (t , tc .want , user )
1365+ }
1366+ for _ , hook := range tc .globalHook {
1367+ callback .GetCallback ().Remove (hook .opType , hook .name )
1368+ }
1369+ finder .beforeHooks = nil
1370+ finder .afterHooks = nil
1371+ })
1372+ }
1373+ }
0 commit comments