-
Notifications
You must be signed in to change notification settings - Fork 378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support $currentDate
field update operator
#662
Changes from all commits
695375d
b2483ba
083fdff
a0ad74d
57521bd
b0a6ca2
5a9760e
84d61f4
2c8c5b3
22aa73a
e29dacb
0ef3500
23b5d28
39d4c5a
730a100
335b2f2
8ad5c9f
bb78d82
88bc6a8
1de73f4
643475b
8318363
2ae3b39
2fd401f
bbf9290
cb7fb1d
d9720f7
6fd1c91
57a864a
c098c36
f5293ff
8f3e8e5
a88696d
45658ef
41971a7
b5094f4
28d4184
2df31f1
c3ba437
53211ed
7c3f006
a0df3f8
1ead17b
ccacfef
72750ed
0532c0f
6adc1e6
4465653
9adda32
868ad12
6ec104e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,14 +17,18 @@ package integration | |
import ( | ||
"math" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.mongodb.org/mongo-driver/bson" | ||
"go.mongodb.org/mongo-driver/bson/primitive" | ||
"go.mongodb.org/mongo-driver/mongo" | ||
"go.mongodb.org/mongo-driver/mongo/options" | ||
|
||
"github.com/FerretDB/FerretDB/integration/shareddata" | ||
"github.com/FerretDB/FerretDB/internal/types" | ||
"github.com/FerretDB/FerretDB/internal/util/testutil" | ||
) | ||
|
||
func TestUpdateUpsert(t *testing.T) { | ||
|
@@ -714,3 +718,211 @@ func TestUpdateMany(t *testing.T) { | |
}) | ||
} | ||
} | ||
|
||
func TestCurrentDate(t *testing.T) { | ||
t.Parallel() | ||
|
||
// maxDifference is a maximum amount of seconds can differ the value in placeholder from actual value | ||
maxDifference := time.Duration(60 * time.Second) | ||
|
||
now := primitive.NewDateTimeFromTime(time.Now().UTC()) | ||
nowTimestamp := primitive.Timestamp{T: uint32(time.Now().UTC().Unix()), I: uint32(0)} | ||
Comment on lines
+728
to
+729
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
for name, tc := range map[string]struct { | ||
id string | ||
update bson.D | ||
result bson.D | ||
paths []types.Path | ||
err *mongo.WriteError | ||
stat *mongo.UpdateResult | ||
alt string | ||
}{ | ||
"DocumentEmpty": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", bson.D{}}}, | ||
stat: &mongo.UpdateResult{ | ||
MatchedCount: 1, | ||
ModifiedCount: 0, | ||
UpsertedCount: 0, | ||
}, | ||
result: bson.D{{"_id", "double"}, {"value", float64(42.13)}}, | ||
}, | ||
"Array": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", bson.A{}}}, | ||
err: &mongo.WriteError{ | ||
Code: 9, | ||
Message: "Modifiers operate on fields but we found type array instead. " + | ||
"For example: {$mod: {<field>: ...}} not {$currentDate: []}", | ||
}, | ||
alt: "Modifiers operate on fields but we found another type instead", | ||
}, | ||
"WrongInt32": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", int32(1)}}, | ||
err: &mongo.WriteError{ | ||
Code: 9, | ||
Message: "Modifiers operate on fields but we found type int instead. " + | ||
"For example: {$mod: {<field>: ...}} not {$currentDate: 1}", | ||
}, | ||
alt: "Modifiers operate on fields but we found another type instead", | ||
}, | ||
"Nil": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", nil}}, | ||
err: &mongo.WriteError{ | ||
Code: 9, | ||
Message: "Modifiers operate on fields but we found type null instead. " + | ||
"For example: {$mod: {<field>: ...}} not {$currentDate: null}", | ||
}, | ||
alt: "Modifiers operate on fields but we found another type instead", | ||
}, | ||
"True": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", bson.D{{"value", true}}}}, | ||
stat: &mongo.UpdateResult{ | ||
MatchedCount: 1, | ||
ModifiedCount: 1, | ||
UpsertedCount: 0, | ||
}, | ||
paths: []types.Path{types.NewPathFromString("value")}, | ||
result: bson.D{{"_id", "double"}, {"value", now}}, | ||
}, | ||
"TwoTrue": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", bson.D{{"value", true}, {"unexistent", true}}}}, | ||
stat: &mongo.UpdateResult{ | ||
MatchedCount: 1, | ||
ModifiedCount: 1, | ||
UpsertedCount: 0, | ||
}, | ||
paths: []types.Path{ | ||
types.NewPathFromString("value"), | ||
types.NewPathFromString("unexistent"), | ||
}, | ||
result: bson.D{{"_id", "double"}, {"value", now}, {"unexistent", now}}, | ||
}, | ||
"False": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", bson.D{{"value", false}}}}, | ||
rumyantseva marked this conversation as resolved.
Show resolved
Hide resolved
|
||
stat: &mongo.UpdateResult{ | ||
MatchedCount: 1, | ||
ModifiedCount: 1, | ||
UpsertedCount: 0, | ||
}, | ||
paths: []types.Path{types.NewPathFromString("value")}, | ||
result: bson.D{{"_id", "double"}, {"value", now}}, | ||
}, | ||
"Int32": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", bson.D{{"value", int32(1)}}}}, | ||
err: &mongo.WriteError{ | ||
Code: 2, | ||
Message: "int is not valid type for $currentDate. Please use a boolean ('true') or a $type expression ({$type: 'timestamp/date'}).", | ||
}, | ||
}, | ||
"Timestamp": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", bson.D{{"value", bson.D{{"$type", "timestamp"}}}}}}, | ||
stat: &mongo.UpdateResult{ | ||
MatchedCount: 1, | ||
ModifiedCount: 1, | ||
UpsertedCount: 0, | ||
}, | ||
paths: []types.Path{types.NewPathFromString("value")}, | ||
result: bson.D{{"_id", "double"}, {"value", nowTimestamp}}, | ||
}, | ||
"TimestampCapitalised": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", bson.D{{"value", bson.D{{"$type", "Timestamp"}}}}}}, | ||
err: &mongo.WriteError{ | ||
Code: 2, | ||
Message: "The '$type' string field is required to be 'date' or 'timestamp': {$currentDate: {field : {$type: 'date'}}}", | ||
}, | ||
alt: "The '$type' string field is required to be 'date' or 'timestamp'", | ||
}, | ||
"Date": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", bson.D{{"value", bson.D{{"$type", "date"}}}}}}, | ||
stat: &mongo.UpdateResult{ | ||
MatchedCount: 1, | ||
ModifiedCount: 1, | ||
UpsertedCount: 0, | ||
}, | ||
paths: []types.Path{types.NewPathFromString("value")}, | ||
result: bson.D{{"_id", "double"}, {"value", now}}, | ||
}, | ||
"WrongType": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", bson.D{{"value", bson.D{{"$type", bson.D{{"abcd", int32(1)}}}}}}}}, | ||
err: &mongo.WriteError{ | ||
Code: 2, | ||
Message: "The '$type' string field is required to be 'date' or 'timestamp': {$currentDate: {field : {$type: 'date'}}}", | ||
}, | ||
alt: "The '$type' string field is required to be 'date' or 'timestamp'", | ||
}, | ||
"NoField": { | ||
id: "double", | ||
update: bson.D{{"$currentDate", bson.D{{"unexsistent", bson.D{{"$type", "date"}}}}}}, | ||
stat: &mongo.UpdateResult{ | ||
MatchedCount: 1, | ||
ModifiedCount: 1, | ||
UpsertedCount: 0, | ||
}, | ||
paths: []types.Path{types.NewPathFromString("unexsistent")}, | ||
result: bson.D{{"_id", "double"}, {"value", 42.13}, {"unexsistent", now}}, | ||
}, | ||
"UnrecognizedOption": { | ||
id: "array", | ||
update: bson.D{{ | ||
"$currentDate", | ||
bson.D{{ | ||
"value", | ||
bson.D{{ | ||
"array", bson.D{{"unexsistent", bson.D{}}}, | ||
}}, | ||
}}, | ||
}}, | ||
err: &mongo.WriteError{ | ||
Code: 2, | ||
Message: "Unrecognized $currentDate option: array", | ||
}, | ||
}, | ||
} { | ||
name, tc := name, tc | ||
t.Run(name, func(t *testing.T) { | ||
t.Parallel() | ||
ctx, collection := setup(t, shareddata.Scalars, shareddata.Composites) | ||
|
||
res, err := collection.UpdateOne(ctx, bson.D{{"_id", tc.id}}, tc.update) | ||
if tc.err != nil { | ||
require.Nil(t, tc.paths) | ||
require.Nil(t, tc.stat) | ||
AssertEqualAltWriteError(t, *tc.err, tc.alt, err) | ||
return | ||
} | ||
require.NoError(t, err) | ||
require.Equal(t, tc.stat, res) | ||
|
||
var actualB bson.D | ||
err = collection.FindOne(ctx, bson.D{{"_id", tc.id}}).Decode(&actualB) | ||
require.NoError(t, err) | ||
|
||
expected := ConvertDocument(t, tc.result) | ||
actual := ConvertDocument(t, actualB) | ||
|
||
for _, path := range tc.paths { | ||
testutil.CompareAndSetByPathTime( | ||
t, | ||
expected, | ||
actual, | ||
maxDifference, | ||
path, | ||
) | ||
Comment on lines
+915
to
+921
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those arguments are short enough to be put on the same line |
||
expected.RemoveByPath(path) | ||
actual.RemoveByPath(path) | ||
Comment on lines
+922
to
+923
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do we set values by path only to remove them on the next line? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There is |
||
} | ||
assert.Equal(t, expected, actual) | ||
}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That values should be that large. We are in a serious trouble if that test runs for 60 seconds